diff --git a/.azdo/pipelines/azure-dev.yml b/.azdo/pipelines/azure-dev.yml
index 728f2e287..e852f1810 100644
--- a/.azdo/pipelines/azure-dev.yml
+++ b/.azdo/pipelines/azure-dev.yml
@@ -1,7 +1,20 @@
# Run when commits are pushed to mainline branch (main or master)
# Set this to the mainline branch you are using
trigger:
- - main
+ branches:
+ include:
+ - main
+ paths:
+ include:
+ - src/*
+ - infra/*
+ - azure.yaml
+ - azure_custom.yaml
+ - .azdo/pipelines/azure-dev.yml
+ exclude:
+ - '*.md'
+ - docs/*
+ - data/*
# Azure Pipelines workflow to deploy to Azure using azd
# To configure required secrets and service connection for connecting to Azure, simply run `azd pipeline config --provider azdo`
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 000000000..a26ed3c68
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,26 @@
+[run]
+source = .
+omit =
+ src/mcp_server/*
+ src/backend/tests/*
+ src/tests/mcp_server/*
+ src/tests/agents/*
+ src/**/__init__.py
+ tests/e2e-test/*
+ */venv/*
+ */env/*
+ */.pytest_cache/*
+ */node_modules/*
+ src/backend/v4/api/router.py
+
+[paths]
+source =
+ src/backend
+ */site-packages
+
+[report]
+exclude_lines =
+ pragma: no cover
+ def __repr__
+ raise AssertionError
+ raise NotImplementedError
\ No newline at end of file
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 000000000..6aa4847d9
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,4 @@
+FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye
+
+# Remove Yarn repository to avoid GPG key expiration issue
+RUN rm -f /etc/apt/sources.list.d/yarn.list
\ No newline at end of file
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index abdf64c8d..be69b1156 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,6 +1,8 @@
{
"name": "Multi Agent Custom Automation Engine Solution Accelerator",
- "image": "mcr.microsoft.com/devcontainers/python:3.11-bullseye",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {"version": "latest"},
"ghcr.io/azure/azure-dev/azd:latest": {},
diff --git a/.flake8 b/.flake8
index 5b72c2abb..b5305d29d 100644
--- a/.flake8
+++ b/.flake8
@@ -1,5 +1,13 @@
[flake8]
max-line-length = 88
extend-ignore = E501
-exclude = .venv, frontend, src/backend/tests
+exclude =
+ .venv,
+ frontend,
+ src/frontend,
+ src/backend/tests,
+ *.tsx,
+ *.ts,
+ *.jsx,
+ *.js
ignore = E203, W503, G004, G200, E402
\ No newline at end of file
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 5ee12705a..3f45b66e1 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2,4 +2,4 @@
# Each line is a file pattern followed by one or more owners.
# These owners will be the default owners for everything in the repo.
-* @Avijit-Microsoft @Roopan-Microsoft @Prajwal-Microsoft @marktayl1 @Fr4nc3 @Vinay-Microsoft @aniaroramsft
+* @Avijit-Microsoft @Roopan-Microsoft @Prajwal-Microsoft @marktayl1 @Fr4nc3 @Vinay-Microsoft @aniaroramsft @toherman-msft @nchandhi @dgp10801
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f1a0d6648..bdaed742d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -32,7 +32,7 @@ updates:
- "*"
- package-ecosystem: "pip"
- directory: "/src/frontend"
+ directory: "/src/App"
schedule:
interval: "monthly"
commit-message:
diff --git a/.github/workflows/agnext-biab-02-containerimage.yml b/.github/workflows/agnext-biab-02-containerimage.yml
index 156e914ae..72877ab69 100644
--- a/.github/workflows/agnext-biab-02-containerimage.yml
+++ b/.github/workflows/agnext-biab-02-containerimage.yml
@@ -16,12 +16,12 @@ jobs:
packages: write
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
# - name: Download deps
# run: |
# curl -fsSL ${{ vars.AUTOGEN_WHL_URL }} -o agnext-biab-02/autogen_core-0.3.dev0-py3-none-any.whl
- name: Log in to the Container registry
- uses: docker/login-action@v3
+ uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
diff --git a/.github/workflows/azd-template-validation.yml b/.github/workflows/azd-template-validation.yml
new file mode 100644
index 000000000..fe9dc4ca4
--- /dev/null
+++ b/.github/workflows/azd-template-validation.yml
@@ -0,0 +1,41 @@
+name: AZD Template Validation
+on:
+ schedule:
+ - cron: '30 1 * * 4' # Every Thursday at 7:00 AM IST (1:30 AM UTC)
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ id-token: write
+ pull-requests: write
+
+jobs:
+ template_validation:
+ runs-on: ubuntu-latest
+ name: azd template validation
+ environment: production
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set timestamp
+ run: echo "HHMM=$(date -u +'%H%M')" >> $GITHUB_ENV
+
+ - uses: microsoft/template-validation-action@v0.4.3
+ with:
+ validateAzd: ${{ vars.TEMPLATE_VALIDATE_AZD }}
+ validateTests: ${{ vars.TEMPLATE_VALIDATE_TESTS }}
+ useDevContainer: ${{ vars.TEMPLATE_USE_DEV_CONTAINER }}
+ id: validation
+ env:
+ AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+ AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ AZURE_ENV_NAME: azd-${{ vars.AZURE_ENV_NAME }}-${{ env.HHMM }}
+ AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
+ AZURE_ENV_OPENAI_LOCATION : ${{ vars.AZURE_AI_DEPLOYMENT_LOCATION }}
+ AZURE_ENV_MODEL_CAPACITY: 1
+ AZURE_ENV_MODEL_4_1_CAPACITY: 1 # keep low to avoid potential quota issues
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: print result
+ run: cat ${{ steps.validation.outputs.resultFile }}
\ No newline at end of file
diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml
index 99f289247..1da05d6e7 100644
--- a/.github/workflows/azure-dev.yml
+++ b/.github/workflows/azure-dev.yml
@@ -1,41 +1,59 @@
-name: Azure Template Validation
+name: Azure Dev Deploy
+
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
- pull-requests: write
jobs:
- template_validation_job:
+ deploy:
runs-on: ubuntu-latest
environment: production
- name: template validation
+ env:
+ AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+ AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
+ AZURE_ENV_OPENAI_LOCATION : ${{ vars.AZURE_AI_DEPLOYMENT_LOCATION }}
+ AZURE_ENV_MODEL_CAPACITY: 1
+ AZURE_ENV_MODEL_4_1_CAPACITY: 1
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
steps:
- # Step 1: Checkout the code from your repository
- - name: Checkout code
- uses: actions/checkout@v4
- # Step 2: Validate the Azure template using microsoft/template-validation-action
- - name: Validate Azure Template
- uses: microsoft/template-validation-action@Latest
- with:
- validateAzd: true
- useDevContainer: false
- id: validation
- env:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }}
- AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }}
- AZURE_ENV_OPENAI_LOCATION : ${{ secrets.AZURE_AI_DEPLOYMENT_LOCATION }}
- AZURE_ENV_MODEL_CAPACITY: 1
- AZURE_ENV_MODEL_4_1_CAPACITY: 1
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
-
- # Step 3: Print the result of the validation
- - name: print result
- run: cat ${{ steps.validation.outputs.resultFile }}
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Set timestamp and env name
+ run: |
+ HHMM=$(date -u +'%H%M')
+ echo "AZURE_ENV_NAME=azd-${{ vars.AZURE_ENV_NAME }}-${HHMM}" >> $GITHUB_ENV
+
+ - name: Install azd
+ uses: Azure/setup-azd@v2
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Login to AZD
+ shell: bash
+ run: |
+ azd auth login \
+ --client-id "$AZURE_CLIENT_ID" \
+ --federated-credential-provider "github" \
+ --tenant-id "$AZURE_TENANT_ID"
+
+ - name: Provision and Deploy
+ shell: bash
+ run: |
+ if ! azd env select "$AZURE_ENV_NAME"; then
+ azd env new "$AZURE_ENV_NAME" --subscription "$AZURE_SUBSCRIPTION_ID" --location "$AZURE_LOCATION" --no-prompt
+ fi
+ azd config set defaults.subscription "$AZURE_SUBSCRIPTION_ID"
+ azd env set AZURE_ENV_OPENAI_LOCATION="$AZURE_ENV_OPENAI_LOCATION"
+ azd up --no-prompt
\ No newline at end of file
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 14ce486fd..e6a86692e 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -14,8 +14,22 @@ name: "CodeQL Advanced"
on:
push:
branches: [ "main", "dev", "demo" ]
+ paths:
+ - 'src/**/*.py'
+ - 'src/**/*.js'
+ - 'src/**/*.ts'
+ - 'src/**/*.tsx'
+ - 'tests/**/*.py'
+ - '.github/workflows/codeql.yml'
pull_request:
branches: [ "main", "dev", "demo" ]
+ paths:
+ - 'src/**/*.py'
+ - 'src/**/*.js'
+ - 'src/**/*.ts'
+ - 'src/**/*.tsx'
+ - 'tests/**/*.py'
+ - '.github/workflows/codeql.yml'
schedule:
- cron: '44 20 * * 2'
@@ -61,7 +75,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v3
+ uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -89,6 +103,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+ uses: github/codeql-action/analyze@v4
with:
- category: "/language:${{matrix.language}}"
+ category: "/language:${{matrix.language}}"
\ No newline at end of file
diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
index 88bf47515..0539e2ff9 100644
--- a/.github/workflows/create-release.yml
+++ b/.github/workflows/create-release.yml
@@ -18,7 +18,7 @@ jobs:
with:
ref: ${{ github.event.workflow_run.head_sha }}
- - uses: codfish/semantic-release-action@v3
+ - uses: codfish/semantic-release-action@v4
id: semantic
with:
tag-format: 'v${version}'
diff --git a/.github/workflows/deploy-orchestrator.yml b/.github/workflows/deploy-orchestrator.yml
new file mode 100644
index 000000000..9d860ad32
--- /dev/null
+++ b/.github/workflows/deploy-orchestrator.yml
@@ -0,0 +1,141 @@
+name: Deployment orchestrator
+
+on:
+ workflow_call:
+ inputs:
+ runner_os:
+ description: 'Runner OS (ubuntu-latest or windows-latest)'
+ required: true
+ type: string
+ azure_location:
+ description: 'Azure Location For Deployment'
+ required: false
+ default: 'australiaeast'
+ type: string
+ resource_group_name:
+ description: 'Resource Group Name (Optional)'
+ required: false
+ default: ''
+ type: string
+ waf_enabled:
+ description: 'Enable WAF'
+ required: false
+ default: false
+ type: boolean
+ EXP:
+ description: 'Enable EXP'
+ required: false
+ default: false
+ type: boolean
+ build_docker_image:
+ description: 'Build And Push Docker Image (Optional)'
+ required: false
+ default: false
+ type: boolean
+ cleanup_resources:
+ description: 'Cleanup Deployed Resources'
+ required: false
+ default: false
+ type: boolean
+ run_e2e_tests:
+ description: 'Run End-to-End Tests'
+ required: false
+ default: 'GoldenPath-Testing'
+ type: string
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID:
+ description: 'Log Analytics Workspace ID (Optional)'
+ required: false
+ default: ''
+ type: string
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID:
+ description: 'AI Project Resource ID (Optional)'
+ required: false
+ default: ''
+ type: string
+ existing_webapp_url:
+ description: 'Existing Container WebApp URL (Skips Deployment)'
+ required: false
+ default: ''
+ type: string
+ trigger_type:
+ description: 'Trigger type (workflow_dispatch, pull_request, schedule)'
+ required: true
+ type: string
+
+env:
+ AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
+
+jobs:
+ docker-build:
+ uses: ./.github/workflows/job-docker-build.yml
+ with:
+ trigger_type: ${{ inputs.trigger_type }}
+ build_docker_image: ${{ inputs.build_docker_image }}
+ secrets: inherit
+
+ deploy:
+ if: "!cancelled() && (needs.docker-build.result == 'success' || needs.docker-build.result == 'skipped') && (inputs.trigger_type != 'workflow_dispatch' || inputs.existing_webapp_url == '' || inputs.existing_webapp_url == null)"
+ needs: docker-build
+ uses: ./.github/workflows/job-deploy.yml
+ with:
+ trigger_type: ${{ inputs.trigger_type }}
+ runner_os: ${{ inputs.runner_os }}
+ azure_location: ${{ inputs.azure_location }}
+ resource_group_name: ${{ inputs.resource_group_name }}
+ waf_enabled: ${{ inputs.waf_enabled }}
+ EXP: ${{ inputs.EXP }}
+ build_docker_image: ${{ inputs.build_docker_image }}
+ existing_webapp_url: ${{ inputs.existing_webapp_url }}
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ docker_image_tag: ${{ needs.docker-build.outputs.IMAGE_TAG }}
+ run_e2e_tests: ${{ inputs.run_e2e_tests }}
+ cleanup_resources: ${{ inputs.cleanup_resources }}
+ secrets: inherit
+
+ e2e-test:
+ if: "!cancelled() && ((needs.deploy.result == 'success' && needs.deploy.outputs.WEBAPP_URL != '') || (inputs.existing_webapp_url != '' && inputs.existing_webapp_url != null)) && (inputs.trigger_type != 'workflow_dispatch' || (inputs.run_e2e_tests != 'None' && inputs.run_e2e_tests != '' && inputs.run_e2e_tests != null))"
+ needs: [docker-build, deploy]
+ uses: ./.github/workflows/test-automation-v2.yml
+ with:
+ MACAE_WEB_URL: ${{ needs.deploy.outputs.WEBAPP_URL || inputs.existing_webapp_url }}
+ MACAE_URL_API: ${{ needs.deploy.outputs.MACAE_URL_API }}
+ MACAE_RG: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
+ TEST_SUITE: ${{ inputs.trigger_type == 'workflow_dispatch' && inputs.run_e2e_tests || 'GoldenPath-Testing' }}
+ secrets: inherit
+
+ cleanup-deployment:
+ if: "!cancelled() && needs.deploy.outputs.RESOURCE_GROUP_NAME != '' && inputs.existing_webapp_url == '' && (inputs.trigger_type != 'workflow_dispatch' || inputs.cleanup_resources)"
+ needs: [docker-build, deploy, e2e-test]
+ uses: ./.github/workflows/job-cleanup-deployment.yml
+ with:
+ runner_os: ${{ inputs.runner_os }}
+ trigger_type: ${{ inputs.trigger_type }}
+ cleanup_resources: ${{ inputs.cleanup_resources }}
+ existing_webapp_url: ${{ inputs.existing_webapp_url }}
+ RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
+ AZURE_LOCATION: ${{ needs.deploy.outputs.AZURE_LOCATION }}
+ AZURE_ENV_OPENAI_LOCATION: ${{ needs.deploy.outputs.AZURE_ENV_OPENAI_LOCATION }}
+ ENV_NAME: ${{ needs.deploy.outputs.ENV_NAME }}
+ IMAGE_TAG: ${{ needs.deploy.outputs.IMAGE_TAG }}
+ secrets: inherit
+
+ send-notification:
+ if: "!cancelled()"
+ needs: [docker-build, deploy, e2e-test, cleanup-deployment]
+ uses: ./.github/workflows/job-send-notification.yml
+ with:
+ trigger_type: ${{ inputs.trigger_type }}
+ waf_enabled: ${{ inputs.waf_enabled }}
+ EXP: ${{ inputs.EXP }}
+ run_e2e_tests: ${{ inputs.run_e2e_tests }}
+ existing_webapp_url: ${{ inputs.existing_webapp_url }}
+ deploy_result: ${{ needs.deploy.result }}
+ e2e_test_result: ${{ needs.e2e-test.result }}
+ CONTAINER_WEB_APPURL: ${{ needs.deploy.outputs.WEBAPP_URL }}
+ RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
+ QUOTA_FAILED: ${{ needs.deploy.outputs.QUOTA_FAILED }}
+ TEST_SUCCESS: ${{ needs.e2e-test.outputs.TEST_SUCCESS }}
+ TEST_REPORT_URL: ${{ needs.e2e-test.outputs.TEST_REPORT_URL }}
+ cleanup_result: ${{ needs.cleanup-deployment.result }}
+ secrets: inherit
diff --git a/.github/workflows/deploy-v2.yml b/.github/workflows/deploy-v2.yml
new file mode 100644
index 000000000..2d7234d68
--- /dev/null
+++ b/.github/workflows/deploy-v2.yml
@@ -0,0 +1,298 @@
+name: Deploy-Test-Cleanup (v2)
+
+permissions:
+ id-token: write
+ contents: read
+ actions: read
+on:
+ workflow_run:
+ workflows: ["Build Docker and Optional Push v4"]
+ types:
+ - completed
+ branches:
+ - main
+ - dev-v4
+ - hotfix
+ workflow_dispatch:
+ inputs:
+ runner_os:
+ description: 'Deployment Environment'
+ required: false
+ type: choice
+ options:
+ - 'codespace'
+ - 'Local'
+ default: 'codespace'
+ azure_location:
+ description: 'Azure Location For Deployment'
+ required: false
+ default: 'australiaeast'
+ type: choice
+ options:
+ - 'australiaeast'
+ - 'centralus'
+ - 'eastasia'
+ - 'eastus2'
+ - 'japaneast'
+ - 'northeurope'
+ - 'southeastasia'
+ - 'uksouth'
+ resource_group_name:
+ description: 'Resource Group Name (Optional)'
+ required: false
+ default: ''
+ type: string
+
+ waf_enabled:
+ description: 'Enable WAF'
+ required: false
+ default: false
+ type: boolean
+ EXP:
+ description: 'Enable EXP'
+ required: false
+ default: false
+ type: boolean
+ build_docker_image:
+ description: 'Build & Push Docker Image (Optional)'
+ required: false
+ default: false
+ type: boolean
+
+ cleanup_resources:
+ description: 'Cleanup Deployed Resources'
+ required: false
+ default: false
+ type: boolean
+
+ run_e2e_tests:
+ description: 'Run End-to-End Tests'
+ required: false
+ default: 'GoldenPath-Testing'
+ type: choice
+ options:
+ - 'GoldenPath-Testing'
+ - 'Smoke-Testing'
+ - 'None'
+
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID:
+ description: 'Log Analytics Workspace ID (Optional)'
+ required: false
+ default: ''
+ type: string
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID:
+ description: 'AI Project Resource ID (Optional)'
+ required: false
+ default: ''
+ type: string
+ existing_webapp_url:
+ description: 'Existing WebApp URL (Skips Deployment)'
+ required: false
+ default: ''
+ type: string
+
+ schedule:
+ - cron: "0 11,23 * * *" # Runs at 11:00 AM and 11:00 PM GMT
+
+jobs:
+ validate-inputs:
+ runs-on: ubuntu-latest
+ outputs:
+ validation_passed: ${{ steps.validate.outputs.passed }}
+ runner_os: ${{ steps.validate.outputs.runner_os }}
+ azure_location: ${{ steps.validate.outputs.azure_location }}
+ resource_group_name: ${{ steps.validate.outputs.resource_group_name }}
+ waf_enabled: ${{ steps.validate.outputs.waf_enabled }}
+ exp: ${{ steps.validate.outputs.exp }}
+ build_docker_image: ${{ steps.validate.outputs.build_docker_image }}
+ cleanup_resources: ${{ steps.validate.outputs.cleanup_resources }}
+ run_e2e_tests: ${{ steps.validate.outputs.run_e2e_tests }}
+ azure_env_log_analytics_workspace_id: ${{ steps.validate.outputs.azure_env_log_analytics_workspace_id }}
+ azure_existing_ai_project_resource_id: ${{ steps.validate.outputs.azure_existing_ai_project_resource_id }}
+ existing_webapp_url: ${{ steps.validate.outputs.existing_webapp_url }}
+ steps:
+ - name: Validate Workflow Input Parameters
+ id: validate
+ shell: bash
+ env:
+ INPUT_RUNNER_OS: ${{ github.event.inputs.runner_os }}
+ INPUT_AZURE_LOCATION: ${{ github.event.inputs.azure_location }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ github.event.inputs.resource_group_name }}
+ INPUT_WAF_ENABLED: ${{ github.event.inputs.waf_enabled }}
+ INPUT_EXP: ${{ github.event.inputs.EXP }}
+ INPUT_BUILD_DOCKER_IMAGE: ${{ github.event.inputs.build_docker_image }}
+ INPUT_CLEANUP_RESOURCES: ${{ github.event.inputs.cleanup_resources }}
+ INPUT_RUN_E2E_TESTS: ${{ github.event.inputs.run_e2e_tests }}
+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ github.event.inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ INPUT_EXISTING_WEBAPP_URL: ${{ github.event.inputs.existing_webapp_url }}
+ run: |
+ echo "๐ Validating workflow input parameters..."
+ VALIDATION_FAILED=false
+
+ # Resolve runner_os from Deployment Environment selection
+ DEPLOY_ENV="${INPUT_RUNNER_OS:-codespace}"
+ if [[ "$DEPLOY_ENV" == "codespace" ]]; then
+ RUNNER_OS="ubuntu-latest"
+ echo "โ
Deployment Environment: 'codespace' โ runner: ubuntu-latest"
+ elif [[ "$DEPLOY_ENV" == "Local" ]]; then
+ RUNNER_OS="windows-latest"
+ echo "โ
Deployment Environment: 'Local' โ runner: windows-latest"
+ else
+ echo "โ ERROR: Deployment Environment must be 'codespace' or 'Local', got: '$DEPLOY_ENV'"
+ VALIDATION_FAILED=true
+ RUNNER_OS="ubuntu-latest"
+ fi
+
+ # Validate azure_location (Azure region format)
+ LOCATION="${INPUT_AZURE_LOCATION:-australiaeast}"
+
+ if [[ ! "$LOCATION" =~ ^[a-z0-9]+$ ]]; then
+ echo "โ ERROR: azure_location '$LOCATION' is invalid. Must contain only lowercase letters and numbers"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
azure_location: '$LOCATION' is valid"
+ fi
+
+ # Validate resource_group_name (Azure naming convention, optional)
+ if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then
+ if [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then
+ echo "โ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period."
+ VALIDATION_FAILED=true
+ elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then
+ echo "โ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters (length: ${#INPUT_RESOURCE_GROUP_NAME})"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
resource_group_name: '$INPUT_RESOURCE_GROUP_NAME' is valid"
+ fi
+ else
+ echo "โ
resource_group_name: Not provided (will be auto-generated)"
+ fi
+
+ # Validate waf_enabled (boolean)
+ WAF_ENABLED="${INPUT_WAF_ENABLED:-false}"
+ if [[ "$WAF_ENABLED" != "true" && "$WAF_ENABLED" != "false" ]]; then
+ echo "โ ERROR: waf_enabled must be 'true' or 'false', got: '$WAF_ENABLED'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
waf_enabled: '$WAF_ENABLED' is valid"
+ fi
+
+ # Validate EXP (boolean)
+ EXP_ENABLED="${INPUT_EXP:-false}"
+ if [[ "$EXP_ENABLED" != "true" && "$EXP_ENABLED" != "false" ]]; then
+ echo "โ ERROR: EXP must be 'true' or 'false', got: '$EXP_ENABLED'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
EXP: '$EXP_ENABLED' is valid"
+ fi
+
+ # Validate build_docker_image (boolean)
+ BUILD_DOCKER="${INPUT_BUILD_DOCKER_IMAGE:-false}"
+ if [[ "$BUILD_DOCKER" != "true" && "$BUILD_DOCKER" != "false" ]]; then
+ echo "โ ERROR: build_docker_image must be 'true' or 'false', got: '$BUILD_DOCKER'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
build_docker_image: '$BUILD_DOCKER' is valid"
+ fi
+
+ # Validate cleanup_resources (boolean)
+ CLEANUP_RESOURCES="${INPUT_CLEANUP_RESOURCES:-false}"
+ if [[ "$CLEANUP_RESOURCES" != "true" && "$CLEANUP_RESOURCES" != "false" ]]; then
+ echo "โ ERROR: cleanup_resources must be 'true' or 'false', got: '$CLEANUP_RESOURCES'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
cleanup_resources: '$CLEANUP_RESOURCES' is valid"
+ fi
+
+ # Validate run_e2e_tests (specific allowed values)
+ TEST_OPTION="${INPUT_RUN_E2E_TESTS:-GoldenPath-Testing}"
+ if [[ "$TEST_OPTION" != "GoldenPath-Testing" && "$TEST_OPTION" != "Smoke-Testing" && "$TEST_OPTION" != "None" ]]; then
+ echo "โ ERROR: run_e2e_tests must be one of: GoldenPath-Testing, Smoke-Testing, None, got: '$TEST_OPTION'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
run_e2e_tests: '$TEST_OPTION' is valid"
+ fi
+
+ # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional, Azure Resource ID format)
+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then
+ echo "โ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
+ echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format"
+ fi
+ else
+ echo "โ
AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Not provided (optional)"
+ fi
+
+ # Validate AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (optional, Azure Resource ID format)
+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/([Mm]icrosoft\.[Mm]achine[Ll]earning[Ss]ervices/([Ww]orkspaces|[Pp]rojects)/[^/]+|[Mm]icrosoft\.[Cc]ognitive[Ss]ervices/[Aa]ccounts/[^/]+/[Pp]rojects/[^/]+)$ ]]; then
+ echo "โ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
+ echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
+ fi
+ else
+ echo "โ
AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Not provided (optional)"
+ fi
+
+ # Validate existing_webapp_url (optional, must start with https)
+ if [[ -n "$INPUT_EXISTING_WEBAPP_URL" ]]; then
+ if [[ ! "$INPUT_EXISTING_WEBAPP_URL" =~ ^https:// ]]; then
+ echo "โ ERROR: existing_webapp_url must start with 'https://', got: '$INPUT_EXISTING_WEBAPP_URL'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
existing_webapp_url: '$INPUT_EXISTING_WEBAPP_URL' is valid"
+ fi
+ else
+ echo "โ
existing_webapp_url: Not provided (will perform deployment)"
+ fi
+
+ # Fail workflow if any validation failed
+ if [[ "$VALIDATION_FAILED" == "true" ]]; then
+ echo ""
+ echo "โ Parameter validation failed. Please correct the errors above and try again."
+ exit 1
+ fi
+
+ echo ""
+ echo "โ
All input parameters validated successfully!"
+
+ # Output validated values
+ echo "passed=true" >> $GITHUB_OUTPUT
+ echo "runner_os=$RUNNER_OS" >> $GITHUB_OUTPUT
+ echo "azure_location=$LOCATION" >> $GITHUB_OUTPUT
+ echo "resource_group_name=$INPUT_RESOURCE_GROUP_NAME" >> $GITHUB_OUTPUT
+ echo "waf_enabled=$WAF_ENABLED" >> $GITHUB_OUTPUT
+ echo "exp=$EXP_ENABLED" >> $GITHUB_OUTPUT
+ echo "build_docker_image=$BUILD_DOCKER" >> $GITHUB_OUTPUT
+ echo "cleanup_resources=$CLEANUP_RESOURCES" >> $GITHUB_OUTPUT
+ echo "run_e2e_tests=$TEST_OPTION" >> $GITHUB_OUTPUT
+ echo "azure_env_log_analytics_workspace_id=$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" >> $GITHUB_OUTPUT
+ echo "azure_existing_ai_project_resource_id=$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" >> $GITHUB_OUTPUT
+ echo "existing_webapp_url=$INPUT_EXISTING_WEBAPP_URL" >> $GITHUB_OUTPUT
+
+ Run:
+ needs: validate-inputs
+ if: needs.validate-inputs.outputs.validation_passed == 'true'
+ uses: ./.github/workflows/deploy-orchestrator.yml
+ with:
+ runner_os: ${{ needs.validate-inputs.outputs.runner_os || 'ubuntu-latest' }}
+ azure_location: ${{ needs.validate-inputs.outputs.azure_location || 'australiaeast' }}
+ resource_group_name: ${{ needs.validate-inputs.outputs.resource_group_name || '' }}
+ waf_enabled: ${{ needs.validate-inputs.outputs.waf_enabled == 'true' }}
+ EXP: ${{ needs.validate-inputs.outputs.exp == 'true' }}
+ build_docker_image: ${{ needs.validate-inputs.outputs.build_docker_image == 'true' }}
+ cleanup_resources: ${{ needs.validate-inputs.outputs.cleanup_resources == 'true' }}
+ run_e2e_tests: ${{ needs.validate-inputs.outputs.run_e2e_tests || 'GoldenPath-Testing' }}
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ needs.validate-inputs.outputs.azure_env_log_analytics_workspace_id || '' }}
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ needs.validate-inputs.outputs.azure_existing_ai_project_resource_id || '' }}
+ existing_webapp_url: ${{ needs.validate-inputs.outputs.existing_webapp_url || '' }}
+ trigger_type: ${{ github.event_name }}
+ secrets: inherit
diff --git a/.github/workflows/deploy-waf.yml b/.github/workflows/deploy-waf.yml
index 187fa8e93..e2bd38a3f 100644
--- a/.github/workflows/deploy-waf.yml
+++ b/.github/workflows/deploy-waf.yml
@@ -1,35 +1,50 @@
-name: Validate WAF Deployment v3
+name: Validate WAF Deployment v4
+permissions:
+ id-token: write
+ contents: read
+ actions: read
on:
push:
branches:
- main
+ paths:
+ - 'src/**'
+ - 'infra/**'
+ - 'azure.yaml'
+ - 'azure_custom.yaml'
+ - '.github/workflows/deploy-waf.yml'
schedule:
- cron: "0 11,23 * * *" # Runs at 11:00 AM and 11:00 PM GMT
jobs:
deploy:
runs-on: ubuntu-latest
+ environment: production
env:
GPT_MIN_CAPACITY: 1
O4_MINI_MIN_CAPACITY: 1
GPT41_MINI_MIN_CAPACITY: 1
steps:
- name: Checkout Code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Quota Check
id: quota-check
+ env:
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ GPT_MIN_CAPACITY: ${{ env.GPT_MIN_CAPACITY }}
+ O4_MINI_MIN_CAPACITY: ${{ env.O4_MINI_MIN_CAPACITY }}
+ GPT41_MINI_MIN_CAPACITY: ${{ env.GPT41_MINI_MIN_CAPACITY }}
+ AZURE_REGIONS: ${{ vars.AZURE_REGIONS }}
run: |
- export AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }}
- export AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }}
- export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }}
- export AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
- export GPT_MIN_CAPACITY="1"
- export O4_MINI_MIN_CAPACITY="1"
- export GPT41_MINI_MIN_CAPACITY="1"
- export AZURE_REGIONS="${{ vars.AZURE_REGIONS }}"
-
chmod +x infra/scripts/checkquota.sh
if ! infra/scripts/checkquota.sh; then
# If quota check fails due to insufficient quota, set the flag
@@ -63,15 +78,6 @@ jobs:
echo "Selected Region: $VALID_REGION"
echo "AZURE_LOCATION=$VALID_REGION" >> $GITHUB_ENV
- - name: Setup Azure CLI
- run: |
- curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
- az --version # Verify installation
-
- - name: Login to Azure
- run: |
- az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}
-
- name: Install Bicep CLI
run: az bicep install
@@ -130,7 +136,7 @@ jobs:
enablePrivateNetworking=true \
enableScalability=true \
createdBy="Pipeline" \
- tags="{'SecurityControl':'Ignore','Purpose':'Deploying and Cleaning Up Resources for Validation','CreatedDate':'$current_date'}"
+ tags="{'Purpose':'Deploying and Cleaning Up Resources for Validation','CreatedDate':'$current_date'}"
- name: Send Notification on Failure
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c2036be6e..202b33473 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -1,13 +1,17 @@
-name: Validate Deployment v3
+name: Validate Deployment v4
+permissions:
+ id-token: write
+ contents: read
+ actions: read
on:
workflow_run:
- workflows: ["Build Docker and Optional Push v3"]
+ workflows: ["Build Docker and Optional Push v4"]
types:
- completed
branches:
- main
- - dev-v3
+ - dev-v4
- hotfix
schedule:
- cron: "0 11,23 * * *" # Runs at 11:00 AM and 11:00 PM GMT
@@ -21,6 +25,7 @@ env:
jobs:
deploy:
runs-on: ubuntu-latest
+ environment: production
outputs:
RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }}
WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }}
@@ -29,20 +34,24 @@ jobs:
CONTAINER_APP: ${{steps.get_backend_url.outputs.CONTAINER_APP}}
steps:
- name: Checkout Code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Quota Check
id: quota-check
+ env:
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ GPT_MIN_CAPACITY: ${{ env.GPT_MIN_CAPACITY }}
+ O4_MINI_MIN_CAPACITY: ${{ env.O4_MINI_MIN_CAPACITY }}
+ GPT41_MINI_MIN_CAPACITY: ${{ env.GPT41_MINI_MIN_CAPACITY }}
+ AZURE_REGIONS: ${{ vars.AZURE_REGIONS }}
run: |
- export AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }}
- export AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }}
- export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }}
- export AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
- export GPT_MIN_CAPACITY="150"
- export O4_MINI_MIN_CAPACITY="50"
- export GPT41_MINI_MIN_CAPACITY="50"
- export AZURE_REGIONS="${{ vars.AZURE_REGIONS }}"
-
chmod +x infra/scripts/checkquota.sh
if ! infra/scripts/checkquota.sh; then
# If quota check fails due to insufficient quota, set the flag
@@ -76,15 +85,6 @@ jobs:
echo "Selected Region: $VALID_REGION"
echo "AZURE_LOCATION=$VALID_REGION" >> $GITHUB_ENV
- - name: Setup Azure CLI
- run: |
- curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
- az --version # Verify installation
-
- - name: Login to Azure
- run: |
- az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}
-
- name: Install Bicep CLI
run: az bicep install
@@ -120,13 +120,13 @@ jobs:
id: deploy
run: |
if [[ "${{ env.BRANCH_NAME }}" == "main" ]]; then
- IMAGE_TAG="latest_v3"
- elif [[ "${{ env.BRANCH_NAME }}" == "dev-v3" ]]; then
- IMAGE_TAG="dev_v3"
+ IMAGE_TAG="latest_v4"
+ elif [[ "${{ env.BRANCH_NAME }}" == "dev-v4" ]]; then
+ IMAGE_TAG="dev_v4"
elif [[ "${{ env.BRANCH_NAME }}" == "hotfix" ]]; then
IMAGE_TAG="hotfix"
else
- IMAGE_TAG="latest_v3"
+ IMAGE_TAG="latest_v4"
fi
# Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ
@@ -146,7 +146,7 @@ jobs:
azureAiServiceLocation='${{ env.AZURE_LOCATION }}' \
gptModelCapacity=50 \
createdBy="Pipeline" \
- tags="{'SecurityControl':'Ignore','Purpose':'Deploying and Cleaning Up Resources for Validation','CreatedDate':'$current_date'}" \
+ tags="{'Purpose':'Deploying and Cleaning Up Resources for Validation','CreatedDate':'$current_date'}" \
--output json
- name: Extract Web App and API App URLs
@@ -183,28 +183,9 @@ jobs:
echo "Running post-deployment script..."
- # Extract required resource names from the deployment
- STORAGE_ACCOUNT=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.Storage/storageAccounts" --query "[0].name" -o tsv)
- AI_SEARCH=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.Search/searchServices" --query "[0].name" -o tsv)
-
- echo "Found Storage Account: $STORAGE_ACCOUNT"
- echo "Found AI Search Service: $AI_SEARCH"
- echo "Backend URL: ${{ steps.get_backend_url.outputs.MACAE_URL_API }}"
-
- # Run upload team config script with parameters
- bash infra/scripts/upload_team_config.sh \
- "${{ steps.get_backend_url.outputs.MACAE_URL_API }}" \
- "data/agent_teams" \
- "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
-
- # Run process sample data script with parameters
- bash infra/scripts/process_sample_data.sh \
- "$STORAGE_ACCOUNT" \
- "sample-dataset" \
- "$AI_SEARCH" \
- "sample-dataset-index" \
- "${{ env.RESOURCE_GROUP_NAME }}" \
- "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+ # Run PowerShell script with 'All' use case automatically selected
+ # Provide two inputs: '1' for keeping current subscription, '5' for All use cases
+ echo -e "1\n5" | pwsh -File infra/scripts/Selecting-Team-Config-And-Data.ps1 -ResourceGroup "${{ env.RESOURCE_GROUP_NAME }}"
echo "=== Post-Deployment Script Completed Successfully ==="
@@ -233,17 +214,19 @@ jobs:
if: always() && needs.deploy.outputs.RESOURCE_GROUP_NAME != ''
needs: [deploy, e2e-test]
runs-on: ubuntu-latest
+ environment: production
env:
RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
steps:
- - name: Setup Azure CLI
- run: |
- curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
- az --version
- name: Login to Azure
- run: |
- az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}
- az account set --subscription "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Set Azure Subscription
+ run: az account set --subscription "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
- name: Extract AI Services and Key Vault Names
if: always()
diff --git a/.github/workflows/docker-build-and-push.yml b/.github/workflows/docker-build-and-push.yml
index eb46863e5..a98de3568 100644
--- a/.github/workflows/docker-build-and-push.yml
+++ b/.github/workflows/docker-build-and-push.yml
@@ -1,12 +1,24 @@
-name: Build Docker and Optional Push v3
+name: Build Docker and Optional Push v4
on:
push:
branches:
- main
- - dev-v3
- - demo-v3
+ - dev-v4
+ - demo-v4
- hotfix
+ paths:
+ - 'src/App/**'
+ - 'src/backend/**'
+ - 'src/mcp_server/**'
+ - '.github/workflows/docker-build-and-push.yml'
+ - 'infra/main.bicep'
+ - 'infra/modules/**/*.bicep'
+ - 'infra/*.parameters.json'
+ - 'infra/scripts/**'
+ - '.github/workflows/deploy.yml'
+ - 'azure.yaml'
+ - 'azure_custom.yaml'
pull_request:
types:
- opened
@@ -15,29 +27,51 @@ on:
- synchronize
branches:
- main
- - dev-v3
- - demo-v3
+ - dev-v4
+ - demo-v4
- hotfix
+ paths:
+ - 'src/App/**'
+ - 'src/backend/**'
+ - 'src/mcp_server/**'
+ - '.github/workflows/docker-build-and-push.yml'
+ - 'infra/main.bicep'
+ - 'infra/modules/**/*.bicep'
+ - 'infra/*.parameters.json'
+ - 'infra/scripts/**'
+ - '.github/workflows/deploy.yml'
+ - 'azure.yaml'
+ - 'azure_custom.yaml'
workflow_dispatch:
+permissions:
+ id-token: write
+ contents: read
+ actions: read
+
jobs:
build-and-push:
runs-on: ubuntu-latest
+ environment: production
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
+ uses: docker/setup-buildx-action@v3
- - name: Log in to Azure Container Registry
- if: ${{ github.ref_name == 'main' || github.ref_name == 'dev-v3'|| github.ref_name == 'demo-v3' || github.ref_name == 'hotfix' }}
- uses: azure/docker-login@v2
+ - name: Login to Azure
+ if: ${{ github.ref_name == 'main' || github.ref_name == 'dev-v4'|| github.ref_name == 'demo-v4' || github.ref_name == 'hotfix' }}
+ uses: azure/login@v2
with:
- login-server: ${{ secrets.ACR_LOGIN_SERVER || 'acrlogin.azurecr.io' }}
- username: ${{ secrets.ACR_USERNAME }}
- password: ${{ secrets.ACR_PASSWORD }}
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Log in to Azure Container Registry
+ if: ${{ github.ref_name == 'main' || github.ref_name == 'dev-v4'|| github.ref_name == 'demo-v4' || github.ref_name == 'hotfix' }}
+ run: az acr login --name ${{ secrets.ACR_LOGIN_SERVER || 'acrlogin.azurecr.io' }}
- name: Get current date
id: date
@@ -46,17 +80,17 @@ jobs:
- name: Get registry
id: registry
run: |
- echo "ext_registry=${{ secrets.ACR_LOGIN_SERVER || 'acrlogin.azurecr.io'}}" >> $GITHUB_OUTPUT
+ echo "ext_registry=${{ secrets.ACR_LOGIN_SERVER || 'acrlogin.azurecr.io' }}" >> $GITHUB_OUTPUT
- name: Determine Tag Name Based on Branch
id: determine_tag
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
- echo "TAG=latest_v3" >> $GITHUB_ENV
- elif [[ "${{ github.ref }}" == "refs/heads/dev-v3" ]]; then
- echo "TAG=dev_v3" >> $GITHUB_ENV
- elif [[ "${{ github.ref }}" == "refs/heads/demo-v3" ]]; then
- echo "TAG=demo_v3" >> $GITHUB_ENV
+ echo "TAG=latest_v4" >> $GITHUB_ENV
+ elif [[ "${{ github.ref }}" == "refs/heads/dev-v4" ]]; then
+ echo "TAG=dev_v4" >> $GITHUB_ENV
+ elif [[ "${{ github.ref }}" == "refs/heads/demo-v4" ]]; then
+ echo "TAG=demo_v4" >> $GITHUB_ENV
elif [[ "${{ github.ref }}" == "refs/heads/hotfix" ]]; then
echo "TAG=hotfix" >> $GITHUB_ENV
else
@@ -83,8 +117,8 @@ jobs:
- name: Build and optionally push Frontend Docker image
uses: docker/build-push-action@v6
with:
- context: ./src/frontend
- file: ./src/frontend/Dockerfile
+ context: ./src/App
+ file: ./src/App/Dockerfile
push: ${{ env.TAG != 'pullrequest-ignore' }}
tags: |
${{ steps.registry.outputs.ext_registry }}/macaefrontend:${{ env.TAG }}
diff --git a/.github/workflows/job-cleanup-deployment.yml b/.github/workflows/job-cleanup-deployment.yml
new file mode 100644
index 000000000..48c2586b0
--- /dev/null
+++ b/.github/workflows/job-cleanup-deployment.yml
@@ -0,0 +1,114 @@
+name: Cleanup Deployment Job
+
+on:
+ workflow_call:
+ inputs:
+ runner_os:
+ description: 'Runner OS (ubuntu-latest or windows-latest)'
+ required: true
+ type: string
+ trigger_type:
+ description: 'Trigger type (workflow_dispatch, pull_request, schedule)'
+ required: true
+ type: string
+ cleanup_resources:
+ description: 'Cleanup Deployed Resources'
+ required: false
+ default: false
+ type: boolean
+ existing_webapp_url:
+ description: 'Existing Container WebApp URL (Skips Deployment)'
+ required: false
+ default: ''
+ type: string
+ RESOURCE_GROUP_NAME:
+ description: 'Resource Group Name to cleanup'
+ required: true
+ type: string
+ AZURE_LOCATION:
+ description: 'Azure Location'
+ required: true
+ type: string
+ AZURE_ENV_OPENAI_LOCATION:
+ description: 'Azure OpenAI Location'
+ required: true
+ type: string
+ ENV_NAME:
+ description: 'Environment Name'
+ required: true
+ type: string
+ IMAGE_TAG:
+ description: 'Docker Image Tag'
+ required: true
+ type: string
+
+jobs:
+ cleanup-deployment:
+ runs-on: ${{ inputs.runner_os }}
+ continue-on-error: true
+ environment: production
+ env:
+ RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ ENV_NAME: ${{ inputs.ENV_NAME }}
+ IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ steps:
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Set Azure Subscription
+ shell: bash
+ run: az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Delete Resource Group (Optimized Cleanup)
+ id: delete_rg
+ shell: bash
+ run: |
+ set -e
+ echo "๐๏ธ Starting optimized resource cleanup..."
+ echo "Deleting resource group: ${{ env.RESOURCE_GROUP_NAME }}"
+
+ az group delete \
+ --name "${{ env.RESOURCE_GROUP_NAME }}" \
+ --yes \
+ --no-wait
+
+ echo "โ
Resource group deletion initiated (running asynchronously)"
+ echo "Note: Resources will be cleaned up in the background"
+
+ - name: Logout from Azure
+ if: always()
+ shell: bash
+ run: |
+ azd auth logout || true
+ az logout || echo "Warning: Failed to logout from Azure CLI"
+ echo "Logged out from Azure."
+
+ - name: Generate Cleanup Job Summary
+ if: always()
+ shell: bash
+ run: |
+ echo "## ๐งน Cleanup Job Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Resource Group deletion Status** | ${{ steps.delete_rg.outcome == 'success' && 'โ
Initiated' || 'โ Failed' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Resource Group** | \`${{ env.RESOURCE_GROUP_NAME }}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [[ "${{ steps.delete_rg.outcome }}" == "success" ]]; then
+ echo "### โ
Cleanup Details" >> $GITHUB_STEP_SUMMARY
+ echo "- Successfully initiated deletion for Resource Group \`${{ env.RESOURCE_GROUP_NAME }}\`" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "### โ Cleanup Failed" >> $GITHUB_STEP_SUMMARY
+ echo "- Cleanup process encountered an error" >> $GITHUB_STEP_SUMMARY
+ echo "- Manual cleanup may be required for:" >> $GITHUB_STEP_SUMMARY
+ echo " - Resource Group: \`${{ env.RESOURCE_GROUP_NAME }}\`" >> $GITHUB_STEP_SUMMARY
+ echo "- Check the cleanup-deployment job logs for detailed error information" >> $GITHUB_STEP_SUMMARY
+ fi
diff --git a/.github/workflows/job-deploy-linux.yml b/.github/workflows/job-deploy-linux.yml
new file mode 100644
index 000000000..60c5458ef
--- /dev/null
+++ b/.github/workflows/job-deploy-linux.yml
@@ -0,0 +1,393 @@
+name: Deploy Steps - Linux
+
+on:
+ workflow_call:
+ inputs:
+ ENV_NAME:
+ required: true
+ type: string
+ AZURE_ENV_OPENAI_LOCATION:
+ required: true
+ type: string
+ AZURE_LOCATION:
+ required: true
+ type: string
+ RESOURCE_GROUP_NAME:
+ required: true
+ type: string
+ IMAGE_TAG:
+ required: true
+ type: string
+ BUILD_DOCKER_IMAGE:
+ required: true
+ type: string
+ EXP:
+ required: true
+ type: string
+ WAF_ENABLED:
+ required: false
+ type: string
+ default: 'false'
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID:
+ required: false
+ type: string
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID:
+ required: false
+ type: string
+ outputs:
+ WEBAPP_URL:
+ description: "Web Application URL"
+ value: ${{ jobs.deploy-linux.outputs.WEBAPP_URL }}
+ MACAE_URL_API:
+ description: "MACAE API URL"
+ value: ${{ jobs.deploy-linux.outputs.MACAE_URL_API }}
+
+jobs:
+ deploy-linux:
+ runs-on: ubuntu-latest
+ environment: production
+ env:
+ AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
+ outputs:
+ WEBAPP_URL: ${{ steps.get_output_linux.outputs.WEBAPP_URL }}
+ MACAE_URL_API: ${{ steps.get_output_linux.outputs.BACKEND_URL }}
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Validate Workflow Input Parameters
+ shell: bash
+ env:
+ INPUT_ENV_NAME: ${{ inputs.ENV_NAME }}
+ INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ INPUT_BUILD_DOCKER_IMAGE: ${{ inputs.BUILD_DOCKER_IMAGE }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }}
+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ run: |
+ echo "๐ Validating workflow input parameters..."
+ VALIDATION_FAILED=false
+
+ # Validate ENV_NAME (required, alphanumeric and hyphens)
+ if [[ -z "$INPUT_ENV_NAME" ]]; then
+ echo "โ ERROR: ENV_NAME is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_ENV_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then
+ echo "โ ERROR: ENV_NAME '$INPUT_ENV_NAME' is invalid. Must contain only alphanumerics and hyphens"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
ENV_NAME: '$INPUT_ENV_NAME' is valid"
+ fi
+
+ # Validate AZURE_ENV_OPENAI_LOCATION (required, Azure region format)
+ if [[ -z "$INPUT_AZURE_ENV_OPENAI_LOCATION" ]]; then
+ echo "โ ERROR: AZURE_ENV_OPENAI_LOCATION is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_AZURE_ENV_OPENAI_LOCATION" =~ ^[a-z0-9]+$ ]]; then
+ echo "โ ERROR: AZURE_ENV_OPENAI_LOCATION '$INPUT_AZURE_ENV_OPENAI_LOCATION' is invalid. Must contain only lowercase letters and numbers"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_ENV_OPENAI_LOCATION: '$INPUT_AZURE_ENV_OPENAI_LOCATION' is valid"
+ fi
+
+ # Validate AZURE_LOCATION (required, Azure region format)
+ if [[ -z "$INPUT_AZURE_LOCATION" ]]; then
+ echo "โ ERROR: AZURE_LOCATION is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then
+ echo "โ ERROR: AZURE_LOCATION '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_LOCATION: '$INPUT_AZURE_LOCATION' is valid"
+ fi
+
+ # Validate RESOURCE_GROUP_NAME (required, Azure naming convention)
+ if [[ -z "$INPUT_RESOURCE_GROUP_NAME" ]]; then
+ echo "โ ERROR: RESOURCE_GROUP_NAME is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then
+ echo "โ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period."
+ VALIDATION_FAILED=true
+ elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then
+ echo "โ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
RESOURCE_GROUP_NAME: '$INPUT_RESOURCE_GROUP_NAME' is valid"
+ fi
+
+ # Validate IMAGE_TAG (required, Docker tag pattern)
+ if [[ -z "$INPUT_IMAGE_TAG" ]]; then
+ echo "โ ERROR: IMAGE_TAG is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then
+ echo "โ ERROR: IMAGE_TAG '$INPUT_IMAGE_TAG' is invalid. Must start with alphanumeric or underscore, max 128 characters"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
IMAGE_TAG: '$INPUT_IMAGE_TAG' is valid"
+ fi
+
+ # Validate BUILD_DOCKER_IMAGE (required, must be 'true' or 'false')
+ if [[ "$INPUT_BUILD_DOCKER_IMAGE" != "true" && "$INPUT_BUILD_DOCKER_IMAGE" != "false" ]]; then
+ echo "โ ERROR: BUILD_DOCKER_IMAGE must be 'true' or 'false', got: '$INPUT_BUILD_DOCKER_IMAGE'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
BUILD_DOCKER_IMAGE: '$INPUT_BUILD_DOCKER_IMAGE' is valid"
+ fi
+
+ # Validate EXP (required, must be 'true' or 'false')
+ if [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then
+ echo "โ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
EXP: '$INPUT_EXP' is valid"
+ fi
+
+ # Validate WAF_ENABLED (must be 'true' or 'false')
+ if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then
+ echo "โ ERROR: WAF_ENABLED must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
WAF_ENABLED: '$INPUT_WAF_ENABLED' is valid"
+ fi
+
+ # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional, if provided must be valid Resource ID)
+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then
+ echo "โ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
+ echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format"
+ fi
+ fi
+
+ # Validate AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (optional, if provided must be valid Resource ID)
+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/([Mm]icrosoft\.[Mm]achine[Ll]earning[Ss]ervices/([Ww]orkspaces|[Pp]rojects)/[^/]+|[Mm]icrosoft\.[Cc]ognitive[Ss]ervices/[Aa]ccounts/[^/]+/[Pp]rojects/[^/]+)$ ]]; then
+ echo "โ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
+ echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
+ fi
+ fi
+
+ # Fail workflow if any validation failed
+ if [[ "$VALIDATION_FAILED" == "true" ]]; then
+ echo ""
+ echo "โ Parameter validation failed. Please correct the errors above and try again."
+ exit 1
+ fi
+
+ echo ""
+ echo "โ
All input parameters validated successfully!"
+
+ - name: Configure Parameters Based on WAF Setting
+ shell: bash
+ env:
+ INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }}
+ run: |
+ if [[ "$INPUT_WAF_ENABLED" == "true" ]]; then
+ cp infra/main.waf.parameters.json infra/main.parameters.json
+ echo "โ
Successfully copied WAF parameters to main parameters file"
+ else
+ echo "๐ง Configuring Non-WAF deployment - using default main.parameters.json..."
+ fi
+
+ - name: Install azd
+ uses: Azure/setup-azd@v2
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Login to AZD
+ id: login-azure
+ shell: bash
+ run: |
+ az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ azd auth login --client-id "${{ secrets.AZURE_CLIENT_ID }}" --federated-credential-provider "github" --tenant-id "${{ secrets.AZURE_TENANT_ID }}"
+
+ - name: Deploy using azd up and extract values (Linux)
+ id: get_output_linux
+ shell: bash
+ env:
+ INPUT_ENV_NAME: ${{ inputs.ENV_NAME }}
+ INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ INPUT_BUILD_DOCKER_IMAGE: ${{ inputs.BUILD_DOCKER_IMAGE }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ run: |
+ set -e
+
+ echo "Creating environment..."
+ azd env new $INPUT_ENV_NAME --no-prompt
+ echo "Environment created: $INPUT_ENV_NAME"
+
+ echo "Setting default subscription..."
+ azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ # Set additional parameters
+ azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+ azd env set AZURE_ENV_OPENAI_LOCATION="$INPUT_AZURE_ENV_OPENAI_LOCATION"
+ azd env set AZURE_LOCATION="$INPUT_AZURE_LOCATION"
+ azd env set AZURE_RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME"
+ azd env set AZURE_ENV_IMAGE_TAG="$INPUT_IMAGE_TAG"
+
+ if [[ "$INPUT_BUILD_DOCKER_IMAGE" == "true" ]]; then
+ ACR_NAME=$(echo "${{ secrets.ACR_TEST_LOGIN_SERVER }}")
+ azd env set AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT="$ACR_NAME"
+ echo "Set ACR name to: $ACR_NAME"
+ else
+ echo "Skipping ACR name configuration (using existing image)"
+ fi
+
+ if [[ "$INPUT_EXP" == "true" ]]; then
+ echo "โ
EXP ENABLED - Setting EXP parameters..."
+
+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
+ EXP_LOG_ANALYTICS_ID="$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID"
+ else
+ EXP_LOG_ANALYTICS_ID="${{ secrets.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}"
+ fi
+
+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
+ EXP_AI_PROJECT_ID="$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID"
+ else
+ EXP_AI_PROJECT_ID="${{ secrets.AZURE_ENV_FOUNDRY_PROJECT_ID }}"
+ fi
+
+ echo "AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: $EXP_LOG_ANALYTICS_ID"
+ echo "AZURE_ENV_FOUNDRY_PROJECT_ID: $EXP_AI_PROJECT_ID"
+ azd env set AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID="$EXP_LOG_ANALYTICS_ID"
+ azd env set AZURE_EXISTING_AI_PROJECT_RESOURCE_ID="$EXP_AI_PROJECT_ID"
+ else
+ echo "EXP DISABLED - Skipping EXP parameters"
+ fi
+
+ azd up --no-prompt
+
+ echo "โ
Deployment succeeded."
+ echo "$DEPLOY_OUTPUT"
+
+ echo "Extracting deployment outputs..."
+ DEPLOY_OUTPUT=$(azd env get-values --output json)
+ echo "Deployment output: $DEPLOY_OUTPUT"
+
+ if [[ -z "$DEPLOY_OUTPUT" ]]; then
+ echo "Error: Deployment output is empty. Please check the deployment logs."
+ exit 1
+ fi
+
+ export AZURE_AI_SEARCH_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.AZURE_AI_SEARCH_NAME // empty')
+ echo "AZURE_AI_SEARCH_NAME=$AZURE_AI_SEARCH_NAME" >> $GITHUB_OUTPUT
+
+ export AZURE_ENV_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.AZURE_ENV_NAME // empty')
+ echo "AZURE_ENV_NAME=$AZURE_ENV_NAME" >> $GITHUB_OUTPUT
+
+ export AZURE_STORAGE_ACCOUNT_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.AZURE_STORAGE_ACCOUNT_NAME // empty')
+ echo "AZURE_STORAGE_ACCOUNT_NAME=$AZURE_STORAGE_ACCOUNT_NAME" >> $GITHUB_OUTPUT
+
+ export BACKEND_URL=$(echo "$DEPLOY_OUTPUT" | jq -r '.BACKEND_URL // empty')
+ echo "BACKEND_URL=$BACKEND_URL" >> $GITHUB_ENV
+ echo "BACKEND_URL=$BACKEND_URL" >> $GITHUB_OUTPUT
+
+ export WEBAPP_URL="https://$(echo "$DEPLOY_OUTPUT" | jq -r '.webSiteDefaultHostname // empty')"
+ echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_ENV
+ echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT
+
+ - name: Run Post deployment scripts
+ env:
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ AZURE_RESOURCE_GROUP: ${{ inputs.RESOURCE_GROUP_NAME }}
+ BACKEND_URL: ${{ steps.get_output_linux.outputs.BACKEND_URL }}
+ AZURE_STORAGE_ACCOUNT_NAME: ${{ steps.get_output_linux.outputs.AZURE_STORAGE_ACCOUNT_NAME }}
+ AZURE_STORAGE_CONTAINER_NAME: sample-dataset
+ AZURE_AI_SEARCH_NAME: ${{ steps.get_output_linux.outputs.AZURE_AI_SEARCH_NAME }}
+ AZURE_AI_SEARCH_INDEX_NAME: sample-dataset-index
+ AZURE_ENV_NAME: ${{ steps.get_output_linux.outputs.AZURE_ENV_NAME }}
+ run: |
+ set -e
+ az account set --subscription "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+
+ # Upload team configurations and index sample data in one step
+ # Automatically select "6" (All use cases) for non-interactive deployment
+ echo "6" | bash infra/scripts/selecting_team_config_and_data.sh
+
+ - name: Generate Deployment Summary
+ if: always()
+ shell: bash
+ env:
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ run: |
+ echo "## ๐ Deploy Job Summary (Linux)" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Job Status** | ${{ job.status == 'success' && 'โ
Success' || 'โ Failed' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Resource Group** | \`$INPUT_RESOURCE_GROUP_NAME\` |" >> $GITHUB_STEP_SUMMARY
+ CONFIG_TYPE=""
+ if [[ "$INPUT_WAF_ENABLED" == "true" && "$INPUT_EXP" == "true" ]]; then
+ CONFIG_TYPE="WAF + EXP"
+ elif [[ "$INPUT_WAF_ENABLED" == "true" && "$INPUT_EXP" != "true" ]]; then
+ CONFIG_TYPE="WAF + Non-EXP"
+ elif [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_EXP" == "true" ]]; then
+ CONFIG_TYPE="Non-WAF + EXP"
+ else
+ CONFIG_TYPE="Non-WAF + Non-EXP"
+ fi
+ echo "| **Configuration Type** | \`$CONFIG_TYPE\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Azure Region (Infrastructure)** | \`$INPUT_AZURE_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Azure OpenAI Region** | \`$INPUT_AZURE_ENV_OPENAI_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Docker Image Tag** | \`$INPUT_IMAGE_TAG\` |" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [[ "${{ job.status }}" == "success" ]]; then
+ echo "### โ
Deployment Details" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Display Web App URL if available
+ if [[ -n "${{ steps.get_output_linux.outputs.WEBAPP_URL }}" ]]; then
+ echo "- **Web Application URL**: [${{ steps.get_output_linux.outputs.WEBAPP_URL }}](${{ steps.get_output_linux.outputs.WEBAPP_URL }})" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ # Display Container App Backend URL if available
+ if [[ -n "${{ steps.get_output_linux.outputs.BACKEND_URL }}" ]]; then
+ echo "- **Backend API URL**: [${{ steps.get_output_linux.outputs.BACKEND_URL }}](${{ steps.get_output_linux.outputs.BACKEND_URL }})" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "#### ๐ฏ Post-Deployment Actions Completed:" >> $GITHUB_STEP_SUMMARY
+ echo "- โ
Azure resources provisioned and configured" >> $GITHUB_STEP_SUMMARY
+ echo "- โ
Agent team configurations uploaded" >> $GITHUB_STEP_SUMMARY
+ echo "- โ
Sample datasets processed and indexed" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "### โ Deployment Failed" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- โ Deployment process encountered an error" >> $GITHUB_STEP_SUMMARY
+ echo "- ๐ Check the deployment steps above for detailed error information" >> $GITHUB_STEP_SUMMARY
+ fi
+ - name: Logout from Azure
+ if: always()
+ shell: bash
+ run: |
+ az logout || true
+ echo "Logged out from Azure."
diff --git a/.github/workflows/job-deploy-windows.yml b/.github/workflows/job-deploy-windows.yml
new file mode 100644
index 000000000..030c9619f
--- /dev/null
+++ b/.github/workflows/job-deploy-windows.yml
@@ -0,0 +1,396 @@
+name: Deploy Steps - Windows
+
+on:
+ workflow_call:
+ inputs:
+ ENV_NAME:
+ required: true
+ type: string
+ AZURE_ENV_OPENAI_LOCATION:
+ required: true
+ type: string
+ AZURE_LOCATION:
+ required: true
+ type: string
+ RESOURCE_GROUP_NAME:
+ required: true
+ type: string
+ IMAGE_TAG:
+ required: true
+ type: string
+ BUILD_DOCKER_IMAGE:
+ required: true
+ type: string
+ EXP:
+ required: true
+ type: string
+ WAF_ENABLED:
+ required: false
+ type: string
+ default: 'false'
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID:
+ required: false
+ type: string
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID:
+ required: false
+ type: string
+ outputs:
+ WEBAPP_URL:
+ description: "Web Application URL"
+ value: ${{ jobs.deploy-windows.outputs.WEBAPP_URL }}
+ MACAE_URL_API:
+ description: "MACAE API URL"
+ value: ${{ jobs.deploy-windows.outputs.MACAE_URL_API }}
+jobs:
+ deploy-windows:
+ runs-on: windows-latest
+ environment: production
+ env:
+ AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
+ outputs:
+ WEBAPP_URL: ${{ steps.get_output_windows.outputs.WEBAPP_URL }}
+ MACAE_URL_API: ${{ steps.get_output_windows.outputs.BACKEND_URL }}
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Validate Workflow Input Parameters
+ shell: bash
+ env:
+ INPUT_ENV_NAME: ${{ inputs.ENV_NAME }}
+ INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ INPUT_BUILD_DOCKER_IMAGE: ${{ inputs.BUILD_DOCKER_IMAGE }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }}
+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ run: |
+ echo "๐ Validating workflow input parameters..."
+ VALIDATION_FAILED=false
+
+ # Validate ENV_NAME (required, alphanumeric and hyphens)
+ if [[ -z "$INPUT_ENV_NAME" ]]; then
+ echo "โ ERROR: ENV_NAME is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_ENV_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then
+ echo "โ ERROR: ENV_NAME '$INPUT_ENV_NAME' is invalid. Must contain only alphanumerics and hyphens"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
ENV_NAME: '$INPUT_ENV_NAME' is valid"
+ fi
+
+ # Validate AZURE_ENV_OPENAI_LOCATION (required, Azure region format)
+ if [[ -z "$INPUT_AZURE_ENV_OPENAI_LOCATION" ]]; then
+ echo "โ ERROR: AZURE_ENV_OPENAI_LOCATION is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_AZURE_ENV_OPENAI_LOCATION" =~ ^[a-z0-9]+$ ]]; then
+ echo "โ ERROR: AZURE_ENV_OPENAI_LOCATION '$INPUT_AZURE_ENV_OPENAI_LOCATION' is invalid. Must contain only lowercase letters and numbers"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_ENV_OPENAI_LOCATION: '$INPUT_AZURE_ENV_OPENAI_LOCATION' is valid"
+ fi
+
+ # Validate AZURE_LOCATION (required, Azure region format)
+ if [[ -z "$INPUT_AZURE_LOCATION" ]]; then
+ echo "โ ERROR: AZURE_LOCATION is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then
+ echo "โ ERROR: AZURE_LOCATION '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_LOCATION: '$INPUT_AZURE_LOCATION' is valid"
+ fi
+
+ # Validate RESOURCE_GROUP_NAME (required, Azure naming convention)
+ if [[ -z "$INPUT_RESOURCE_GROUP_NAME" ]]; then
+ echo "โ ERROR: RESOURCE_GROUP_NAME is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then
+ echo "โ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period."
+ VALIDATION_FAILED=true
+ elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then
+ echo "โ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
RESOURCE_GROUP_NAME: '$INPUT_RESOURCE_GROUP_NAME' is valid"
+ fi
+
+ # Validate IMAGE_TAG (required, Docker tag pattern)
+ if [[ -z "$INPUT_IMAGE_TAG" ]]; then
+ echo "โ ERROR: IMAGE_TAG is required but not provided"
+ VALIDATION_FAILED=true
+ elif [[ ! "$INPUT_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then
+ echo "โ ERROR: IMAGE_TAG '$INPUT_IMAGE_TAG' is invalid. Must start with alphanumeric or underscore, max 128 characters"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
IMAGE_TAG: '$INPUT_IMAGE_TAG' is valid"
+ fi
+
+ # Validate BUILD_DOCKER_IMAGE (required, must be 'true' or 'false')
+ if [[ "$INPUT_BUILD_DOCKER_IMAGE" != "true" && "$INPUT_BUILD_DOCKER_IMAGE" != "false" ]]; then
+ echo "โ ERROR: BUILD_DOCKER_IMAGE must be 'true' or 'false', got: '$INPUT_BUILD_DOCKER_IMAGE'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
BUILD_DOCKER_IMAGE: '$INPUT_BUILD_DOCKER_IMAGE' is valid"
+ fi
+
+ # Validate EXP (required, must be 'true' or 'false')
+ if [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then
+ echo "โ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
EXP: '$INPUT_EXP' is valid"
+ fi
+
+ # Validate WAF_ENABLED (must be 'true' or 'false')
+ if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then
+ echo "โ ERROR: WAF_ENABLED must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
WAF_ENABLED: '$INPUT_WAF_ENABLED' is valid"
+ fi
+
+ # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional, if provided must be valid Resource ID)
+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then
+ echo "โ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
+ echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format"
+ fi
+ fi
+
+ # Validate AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (optional, if provided must be valid Resource ID)
+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/([Mm]icrosoft\.[Mm]achine[Ll]earning[Ss]ervices/([Ww]orkspaces|[Pp]rojects)/[^/]+|[Mm]icrosoft\.[Cc]ognitive[Ss]ervices/[Aa]ccounts/[^/]+/[Pp]rojects/[^/]+)$ ]]; then
+ echo "โ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
+ echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
+ fi
+ fi
+
+ # Fail workflow if any validation failed
+ if [[ "$VALIDATION_FAILED" == "true" ]]; then
+ echo ""
+ echo "โ Parameter validation failed. Please correct the errors above and try again."
+ exit 1
+ fi
+
+ echo ""
+ echo "โ
All input parameters validated successfully!"
+
+ - name: Configure Parameters Based on WAF Setting
+ shell: bash
+ env:
+ INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }}
+ run: |
+ if [[ "$INPUT_WAF_ENABLED" == "true" ]]; then
+ cp infra/main.waf.parameters.json infra/main.parameters.json
+ echo "โ
Successfully copied WAF parameters to main parameters file"
+ else
+ echo "๐ง Configuring Non-WAF deployment - using default main.parameters.json..."
+ fi
+
+ - name: Install azd
+ uses: Azure/setup-azd@v2
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Login to AZD
+ id: login-azure
+ shell: bash
+ run: |
+ az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ azd auth login --client-id "${{ secrets.AZURE_CLIENT_ID }}" --federated-credential-provider "github" --tenant-id "${{ secrets.AZURE_TENANT_ID }}"
+
+
+ - name: Deploy using azd up and extract values (Windows)
+ id: get_output_windows
+ shell: pwsh
+ env:
+ INPUT_ENV_NAME: ${{ inputs.ENV_NAME }}
+ INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ INPUT_BUILD_DOCKER_IMAGE: ${{ inputs.BUILD_DOCKER_IMAGE }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ run: |
+ $ErrorActionPreference = "Stop"
+ Write-Host "Starting azd deployment..."
+
+ Write-Host "Creating environment..."
+ azd env new $env:INPUT_ENV_NAME --no-prompt
+ Write-Host "Environment created: $env:INPUT_ENV_NAME"
+
+ Write-Host "Setting default subscription..."
+ azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ # Set additional parameters
+ azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+ azd env set AZURE_ENV_OPENAI_LOCATION="$env:INPUT_AZURE_ENV_OPENAI_LOCATION"
+ azd env set AZURE_LOCATION="$env:INPUT_AZURE_LOCATION"
+ azd env set AZURE_RESOURCE_GROUP="$env:INPUT_RESOURCE_GROUP_NAME"
+ azd env set AZURE_ENV_IMAGE_TAG="$env:INPUT_IMAGE_TAG"
+
+ # Set ACR name only when building Docker image
+ if ($env:INPUT_BUILD_DOCKER_IMAGE -eq "true") {
+ $ACR_NAME = "${{ secrets.ACR_TEST_LOGIN_SERVER }}"
+ azd env set AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT="$ACR_NAME"
+ Write-Host "Set ACR name to: $ACR_NAME"
+ } else {
+ Write-Host "Skipping ACR name configuration (using existing image)"
+ }
+
+ if ($env:INPUT_EXP -eq "true") {
+ Write-Host "โ
EXP ENABLED - Setting EXP parameters..."
+
+ # Set EXP variables dynamically
+ if ($env:INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID -ne "") {
+ $EXP_LOG_ANALYTICS_ID = $env:INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID
+ } else {
+ $EXP_LOG_ANALYTICS_ID = "${{ secrets.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}"
+ }
+
+ if ($env:INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID -ne "") {
+ $EXP_AI_PROJECT_ID = $env:INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID
+ } else {
+ $EXP_AI_PROJECT_ID = "${{ secrets.AZURE_ENV_FOUNDRY_PROJECT_ID }}"
+ }
+
+ Write-Host "AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: $EXP_LOG_ANALYTICS_ID"
+ Write-Host "AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: $EXP_AI_PROJECT_ID"
+ azd env set AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID="$EXP_LOG_ANALYTICS_ID"
+ azd env set AZURE_EXISTING_AI_PROJECT_RESOURCE_ID="$EXP_AI_PROJECT_ID"
+ } else {
+ Write-Host "EXP DISABLED - Skipping EXP parameters"
+ }
+
+ # Deploy using azd up
+ azd up --no-prompt
+ Write-Host "โ
Deployment succeeded."
+
+ # Get deployment outputs using azd
+ Write-Host "Extracting deployment outputs..."
+ $DEPLOY_OUTPUT = azd env get-values --output json | ConvertFrom-Json
+ Write-Host "Deployment output: $($DEPLOY_OUTPUT | ConvertTo-Json -Depth 10)"
+
+ if (-not $DEPLOY_OUTPUT) {
+ Write-Host "Error: Deployment output is empty. Please check the deployment logs."
+ exit 1
+ }
+
+ # Export variables only after successful deployment
+ $AZURE_AI_SEARCH_NAME = $DEPLOY_OUTPUT.AZURE_AI_SEARCH_NAME
+ "AZURE_AI_SEARCH_NAME=$AZURE_AI_SEARCH_NAME" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
+
+ $AZURE_ENV_NAME = $DEPLOY_OUTPUT.AZURE_ENV_NAME
+ "AZURE_ENV_NAME=$AZURE_ENV_NAME" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
+
+ $AZURE_STORAGE_ACCOUNT_NAME = $DEPLOY_OUTPUT.AZURE_STORAGE_ACCOUNT_NAME
+ "AZURE_STORAGE_ACCOUNT_NAME=$AZURE_STORAGE_ACCOUNT_NAME" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
+
+ $BACKEND_URL = $DEPLOY_OUTPUT.BACKEND_URL
+ "BACKEND_URL=$BACKEND_URL" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+ "BACKEND_URL=$BACKEND_URL" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
+
+ $WEBAPP_URL = "https://$($DEPLOY_OUTPUT.webSiteDefaultHostname)"
+ "WEBAPP_URL=$WEBAPP_URL" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+ "WEBAPP_URL=$WEBAPP_URL" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
+
+ - name: Run Post deployment scripts
+ shell: bash
+ env:
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ AZURE_RESOURCE_GROUP: ${{ inputs.RESOURCE_GROUP_NAME }}
+ BACKEND_URL: ${{ steps.get_output_windows.outputs.BACKEND_URL }}
+ AZURE_STORAGE_ACCOUNT_NAME: ${{ steps.get_output_windows.outputs.AZURE_STORAGE_ACCOUNT_NAME }}
+ AZURE_STORAGE_CONTAINER_NAME: sample-dataset
+ AZURE_AI_SEARCH_NAME: ${{ steps.get_output_windows.outputs.AZURE_AI_SEARCH_NAME }}
+ AZURE_AI_SEARCH_INDEX_NAME: sample-dataset-index
+ AZURE_ENV_NAME: ${{ steps.get_output_windows.outputs.AZURE_ENV_NAME }}
+ run: |
+ set -e
+ az account set --subscription "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+
+ # Upload team configurations and index sample data in one step
+ # Automatically select "6" (All use cases) for non-interactive deployment
+ echo "6" | bash infra/scripts/selecting_team_config_and_data.sh
+
+ - name: Generate Deployment Summary
+ if: always()
+ shell: bash
+ env:
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }}
+ INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
+ INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }}
+ run: |
+ echo "## ๐ Deploy Job Summary (Windows)" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Job Status** | ${{ job.status == 'success' && 'โ
Success' || 'โ Failed' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Resource Group** | \`$INPUT_RESOURCE_GROUP_NAME\` |" >> $GITHUB_STEP_SUMMARY
+ CONFIG_TYPE="Non-WAF + Non-EXP"
+ if [[ "$INPUT_WAF_ENABLED" == "true" && "$INPUT_EXP" == "true" ]]; then
+ CONFIG_TYPE="WAF + EXP"
+ elif [[ "$INPUT_WAF_ENABLED" == "true" && "$INPUT_EXP" != "true" ]]; then
+ CONFIG_TYPE="WAF + Non-EXP"
+ elif [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_EXP" == "true" ]]; then
+ CONFIG_TYPE="Non-WAF + EXP"
+ fi
+ echo "| **Configuration Type** | \`$CONFIG_TYPE\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Azure Region (Infrastructure)** | \`$INPUT_AZURE_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Azure OpenAI Region** | \`$INPUT_AZURE_ENV_OPENAI_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Docker Image Tag** | \`$INPUT_IMAGE_TAG\` |" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [[ "${{ job.status }}" == "success" ]]; then
+ echo "### โ
Deployment Details" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Display Web App URL if available
+ if [[ -n "${{ steps.get_output_windows.outputs.WEBAPP_URL }}" ]]; then
+ echo "- **Web Application URL**: [${{ steps.get_output_windows.outputs.WEBAPP_URL }}](${{ steps.get_output_windows.outputs.WEBAPP_URL }})" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ # Display Container App Backend URL if available
+ if [[ -n "${{ steps.get_output_windows.outputs.BACKEND_URL }}" ]]; then
+ echo "- **Backend API URL**: [${{ steps.get_output_windows.outputs.BACKEND_URL }}](${{ steps.get_output_windows.outputs.BACKEND_URL }})" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "#### ๐ฏ Post-Deployment Actions Completed:" >> $GITHUB_STEP_SUMMARY
+ echo "- โ
Azure resources provisioned and configured" >> $GITHUB_STEP_SUMMARY
+ echo "- โ
Agent team configurations uploaded" >> $GITHUB_STEP_SUMMARY
+ echo "- โ
Sample datasets processed and indexed" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "### โ Deployment Failed" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- โ Deployment process encountered an error" >> $GITHUB_STEP_SUMMARY
+ echo "- ๐ Check the deployment steps above for detailed error information" >> $GITHUB_STEP_SUMMARY
+ fi
+ - name: Logout from Azure
+ if: always()
+ shell: bash
+ run: |
+ az logout || true
+ echo "Logged out from Azure."
diff --git a/.github/workflows/job-deploy.yml b/.github/workflows/job-deploy.yml
new file mode 100644
index 000000000..7a8f32e31
--- /dev/null
+++ b/.github/workflows/job-deploy.yml
@@ -0,0 +1,525 @@
+name: Deploy Job
+
+on:
+ workflow_call:
+ inputs:
+ trigger_type:
+ description: 'Trigger type (workflow_dispatch, pull_request, schedule)'
+ required: true
+ type: string
+ runner_os:
+ description: 'Runner OS (ubuntu-latest or windows-latest)'
+ required: true
+ type: string
+ azure_location:
+ description: 'Azure Location For Deployment'
+ required: false
+ default: 'australiaeast'
+ type: string
+ resource_group_name:
+ description: 'Resource Group Name (Optional)'
+ required: false
+ default: ''
+ type: string
+ waf_enabled:
+ description: 'Enable WAF'
+ required: false
+ default: false
+ type: boolean
+ EXP:
+ description: 'Enable EXP'
+ required: false
+ default: false
+ type: boolean
+ build_docker_image:
+ description: 'Build And Push Docker Image (Optional)'
+ required: false
+ default: false
+ type: boolean
+ cleanup_resources:
+ description: 'Cleanup Deployed Resources'
+ required: false
+ default: false
+ type: boolean
+ run_e2e_tests:
+ description: 'Run End-to-End Tests'
+ required: false
+ default: 'GoldenPath-Testing'
+ type: string
+ existing_webapp_url:
+ description: 'Existing Container WebApp URL (Skips Deployment)'
+ required: false
+ default: ''
+ type: string
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID:
+ description: 'Log Analytics Workspace ID (Optional)'
+ required: false
+ default: ''
+ type: string
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID:
+ description: 'AI Project Resource ID (Optional)'
+ required: false
+ default: ''
+ type: string
+ docker_image_tag:
+ description: 'Docker Image Tag from build job'
+ required: false
+ default: ''
+ type: string
+ outputs:
+ RESOURCE_GROUP_NAME:
+ description: "Resource Group Name"
+ value: ${{ jobs.azure-setup.outputs.RESOURCE_GROUP_NAME }}
+ WEBAPP_URL:
+ description: "Container Web App URL"
+ value: ${{ jobs.deploy-linux.outputs.WEBAPP_URL || jobs.deploy-windows.outputs.WEBAPP_URL }}
+ MACAE_URL_API:
+ description: "MACAE API URL"
+ value: ${{ jobs.deploy-linux.outputs.MACAE_URL_API || jobs.deploy-windows.outputs.MACAE_URL_API }}
+ ENV_NAME:
+ description: "Environment Name"
+ value: ${{ jobs.azure-setup.outputs.ENV_NAME }}
+ AZURE_LOCATION:
+ description: "Azure Location"
+ value: ${{ jobs.azure-setup.outputs.AZURE_LOCATION }}
+ AZURE_ENV_OPENAI_LOCATION:
+ description: "Azure OpenAI Location"
+ value: ${{ jobs.azure-setup.outputs.AZURE_ENV_OPENAI_LOCATION }}
+ IMAGE_TAG:
+ description: "Docker Image Tag Used"
+ value: ${{ jobs.azure-setup.outputs.IMAGE_TAG }}
+ QUOTA_FAILED:
+ description: "Quota Check Failed Flag"
+ value: ${{ jobs.azure-setup.outputs.QUOTA_FAILED }}
+
+env:
+ GPT_MIN_CAPACITY: 150
+ O4_MINI_MIN_CAPACITY: 50
+ GPT41_MINI_MIN_CAPACITY: 50
+ BRANCH_NAME: ${{ github.event.workflow_run.head_branch || github.head_ref || github.ref_name }}
+ WAF_ENABLED: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.waf_enabled || false) || false }}
+ EXP: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.EXP || false) || false }}
+ CLEANUP_RESOURCES: ${{ inputs.trigger_type != 'workflow_dispatch' || inputs.cleanup_resources }}
+ RUN_E2E_TESTS: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.run_e2e_tests || 'GoldenPath-Testing') || 'GoldenPath-Testing' }}
+ BUILD_DOCKER_IMAGE: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.build_docker_image || false) || false }}
+
+jobs:
+ azure-setup:
+ name: Azure Setup
+ if: inputs.trigger_type != 'workflow_dispatch' || inputs.existing_webapp_url == '' || inputs.existing_webapp_url == null
+ runs-on: ubuntu-latest
+ environment: production
+ outputs:
+ RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }}
+ ENV_NAME: ${{ steps.generate_env_name.outputs.ENV_NAME }}
+ AZURE_LOCATION: ${{ steps.set_region.outputs.AZURE_LOCATION }}
+ AZURE_ENV_OPENAI_LOCATION: ${{ steps.set_region.outputs.AZURE_ENV_OPENAI_LOCATION }}
+ IMAGE_TAG: ${{ steps.determine_image_tag.outputs.IMAGE_TAG }}
+ QUOTA_FAILED: ${{ steps.quota_failure_output.outputs.QUOTA_FAILED }}
+ EXP_ENABLED: ${{ steps.configure_exp.outputs.EXP_ENABLED }}
+
+ steps:
+ - name: Validate Workflow Input Parameters
+ shell: bash
+ env:
+ INPUT_AZURE_LOCATION: ${{ inputs.azure_location }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }}
+ INPUT_WAF_ENABLED: ${{ inputs.waf_enabled }}
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_CLEANUP_RESOURCES: ${{ inputs.cleanup_resources }}
+ INPUT_RUN_E2E_TESTS: ${{ inputs.run_e2e_tests }}
+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
+ INPUT_DOCKER_IMAGE_TAG: ${{ inputs.docker_image_tag }}
+ run: |
+ echo "๐ Validating workflow input parameters..."
+ VALIDATION_FAILED=false
+
+ # Validate azure_location (Azure region format)
+ if [[ -n "$INPUT_AZURE_LOCATION" ]]; then
+ if [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then
+ echo "โ ERROR: azure_location '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers (e.g., 'australiaeast', 'westus2')"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
azure_location: '$INPUT_AZURE_LOCATION' is valid"
+ fi
+ fi
+
+ # Validate resource_group_name (Azure resource group naming convention)
+ if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then
+ if [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then
+ echo "โ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period."
+ VALIDATION_FAILED=true
+ elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then
+ echo "โ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
resource_group_name: '$INPUT_RESOURCE_GROUP_NAME' is valid"
+ fi
+ fi
+
+ # Validate waf_enabled (boolean)
+ if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then
+ echo "โ ERROR: waf_enabled must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
waf_enabled: '$INPUT_WAF_ENABLED' is valid"
+ fi
+
+ # Validate EXP (boolean)
+ if [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then
+ echo "โ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
EXP: '$INPUT_EXP' is valid"
+ fi
+
+ # Validate cleanup_resources (boolean)
+ if [[ "$INPUT_CLEANUP_RESOURCES" != "true" && "$INPUT_CLEANUP_RESOURCES" != "false" ]]; then
+ echo "โ ERROR: cleanup_resources must be 'true' or 'false', got: '$INPUT_CLEANUP_RESOURCES'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
cleanup_resources: '$INPUT_CLEANUP_RESOURCES' is valid"
+ fi
+
+ # Validate run_e2e_tests (specific allowed values)
+ if [[ -n "$INPUT_RUN_E2E_TESTS" ]]; then
+ ALLOWED_VALUES=("None" "GoldenPath-Testing" "Smoke-Testing")
+ if [[ ! " ${ALLOWED_VALUES[@]} " =~ " ${INPUT_RUN_E2E_TESTS} " ]]; then
+ echo "โ ERROR: run_e2e_tests '$INPUT_RUN_E2E_TESTS' is invalid. Allowed values: ${ALLOWED_VALUES[*]}"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
run_e2e_tests: '$INPUT_RUN_E2E_TESTS' is valid"
+ fi
+ fi
+
+ # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (Azure Resource ID format)
+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then
+ echo "โ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
+ echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format"
+ fi
+ fi
+
+ # Validate AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (Azure Resource ID format)
+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
+ if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/([Mm]icrosoft\.[Mm]achine[Ll]earning[Ss]ervices/([Ww]orkspaces|[Pp]rojects)/[^/]+|[Mm]icrosoft\.[Cc]ognitive[Ss]ervices/[Aa]ccounts/[^/]+/[Pp]rojects/[^/]+)$ ]]; then
+ echo "โ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
+ echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
+ fi
+ fi
+
+ # Validate existing_webapp_url (must start with https)
+ if [[ -n "$INPUT_EXISTING_WEBAPP_URL" ]]; then
+ if [[ ! "$INPUT_EXISTING_WEBAPP_URL" =~ ^https:// ]]; then
+ echo "โ ERROR: existing_webapp_url must start with 'https://', got: '$INPUT_EXISTING_WEBAPP_URL'"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
existing_webapp_url: '$INPUT_EXISTING_WEBAPP_URL' is valid"
+ fi
+ fi
+
+ # Validate docker_image_tag (Docker tag pattern)
+ if [[ -n "$INPUT_DOCKER_IMAGE_TAG" ]]; then
+ # Docker tags: lowercase and uppercase letters, digits, underscores, periods, and hyphens
+ # Cannot start with period or hyphen, max 128 characters
+ if [[ ! "$INPUT_DOCKER_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then
+ echo "โ ERROR: docker_image_tag '$INPUT_DOCKER_IMAGE_TAG' is invalid. Must:"
+ echo " - Start with alphanumeric or underscore"
+ echo " - Contain only alphanumerics, underscores, periods, hyphens"
+ echo " - Be max 128 characters"
+ VALIDATION_FAILED=true
+ else
+ echo "โ
docker_image_tag: '$INPUT_DOCKER_IMAGE_TAG' is valid"
+ fi
+ fi
+
+ # Fail workflow if any validation failed
+ if [[ "$VALIDATION_FAILED" == "true" ]]; then
+ echo ""
+ echo "โ Parameter validation failed. Please correct the errors above and try again."
+ exit 1
+ fi
+
+ echo ""
+ echo "โ
All input parameters validated successfully!"
+
+ - name: Validate and Auto-Configure EXP
+ id: configure_exp
+ shell: bash
+ env:
+ INPUT_EXP: ${{ inputs.EXP }}
+ INPUT_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ INPUT_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ run: |
+ echo "๐ Validating EXP configuration..."
+
+ EXP_ENABLED="false"
+
+ if [[ "$INPUT_EXP" == "true" ]]; then
+ EXP_ENABLED="true"
+ echo "โ
EXP explicitly enabled by user input"
+ elif [[ -n "$INPUT_LOG_ANALYTICS_WORKSPACE_ID" ]] || [[ -n "$INPUT_AI_PROJECT_RESOURCE_ID" ]]; then
+ echo "๐ง AUTO-ENABLING EXP: EXP parameter values were provided but EXP was not explicitly enabled."
+ echo ""
+ echo "You provided values for:"
+ [[ -n "$INPUT_LOG_ANALYTICS_WORKSPACE_ID" ]] && echo " - Azure Log Analytics Workspace ID: '$INPUT_LOG_ANALYTICS_WORKSPACE_ID'"
+ [[ -n "$INPUT_AI_PROJECT_RESOURCE_ID" ]] && echo " - Azure AI Project Resource ID: '$INPUT_AI_PROJECT_RESOURCE_ID'"
+ echo ""
+ echo "โ
Automatically enabling EXP to use these values."
+ EXP_ENABLED="true"
+ fi
+
+ echo "EXP_ENABLED=$EXP_ENABLED" >> $GITHUB_ENV
+ echo "EXP_ENABLED=$EXP_ENABLED" >> $GITHUB_OUTPUT
+ echo "Final EXP status: $EXP_ENABLED"
+
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Set Azure Subscription
+ shell: bash
+ run: az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Run Quota Check
+ id: quota-check
+ env:
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ GPT_MIN_CAPACITY: ${{ env.GPT_MIN_CAPACITY }}
+ O4_MINI_MIN_CAPACITY: ${{ env.O4_MINI_MIN_CAPACITY }}
+ GPT41_MINI_MIN_CAPACITY: ${{ env.GPT41_MINI_MIN_CAPACITY }}
+ AZURE_REGIONS: ${{ vars.AZURE_REGIONS }}
+ run: |
+ chmod +x infra/scripts/checkquota.sh
+ if ! infra/scripts/checkquota.sh; then
+ # If quota check fails due to insufficient quota, set the flag
+ if grep -q "No region with sufficient quota found" infra/scripts/checkquota.sh; then
+ echo "QUOTA_FAILED=true" >> $GITHUB_ENV
+ fi
+ exit 1 # Fail the pipeline if any other failure occurs
+ fi
+
+ - name: Set Quota Failure Output
+ id: quota_failure_output
+ if: env.QUOTA_FAILED == 'true'
+ shell: bash
+ run: |
+ echo "QUOTA_FAILED=true" >> $GITHUB_OUTPUT
+ echo "Quota check failed - will notify via separate notification job"
+
+ - name: Fail Pipeline if Quota Check Fails
+ if: env.QUOTA_FAILED == 'true'
+ shell: bash
+ run: exit 1
+
+ - name: Set Deployment Region
+ id: set_region
+ shell: bash
+ env:
+ INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }}
+ INPUT_AZURE_LOCATION: ${{ inputs.azure_location }}
+ run: |
+ echo "Selected Region from Quota Check: $VALID_REGION"
+ echo "AZURE_ENV_OPENAI_LOCATION=$VALID_REGION" >> $GITHUB_ENV
+ echo "AZURE_ENV_OPENAI_LOCATION=$VALID_REGION" >> $GITHUB_OUTPUT
+
+ if [[ "$INPUT_TRIGGER_TYPE" == "workflow_dispatch" && -n "$INPUT_AZURE_LOCATION" ]]; then
+ USER_SELECTED_LOCATION="$INPUT_AZURE_LOCATION"
+ echo "Using user-selected Azure location: $USER_SELECTED_LOCATION"
+ echo "AZURE_LOCATION=$USER_SELECTED_LOCATION" >> $GITHUB_ENV
+ echo "AZURE_LOCATION=$USER_SELECTED_LOCATION" >> $GITHUB_OUTPUT
+ else
+ echo "Using location from quota check for automatic triggers: $VALID_REGION"
+ echo "AZURE_LOCATION=$VALID_REGION" >> $GITHUB_ENV
+ echo "AZURE_LOCATION=$VALID_REGION" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Generate Resource Group Name
+ id: generate_rg_name
+ shell: bash
+ env:
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }}
+ run: |
+ # Check if a resource group name was provided as input
+ if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then
+ echo "Using provided Resource Group name: $INPUT_RESOURCE_GROUP_NAME"
+ echo "RESOURCE_GROUP_NAME=$INPUT_RESOURCE_GROUP_NAME" >> $GITHUB_ENV
+ else
+ echo "Generating a unique resource group name..."
+ ACCL_NAME="macaev4"
+ SHORT_UUID=$(uuidgen | cut -d'-' -f1)
+ UNIQUE_RG_NAME="arg-${ACCL_NAME}-${SHORT_UUID}"
+ echo "RESOURCE_GROUP_NAME=${UNIQUE_RG_NAME}" >> $GITHUB_ENV
+ echo "Generated RESOURCE_GROUP_NAME: ${UNIQUE_RG_NAME}"
+ fi
+
+ - name: Install Bicep CLI
+ shell: bash
+ run: az bicep install
+
+ - name: Check and Create Resource Group
+ id: check_create_rg
+ shell: bash
+ run: |
+ set -e
+ echo "๐ Checking if resource group '$RESOURCE_GROUP_NAME' exists..."
+ rg_exists=$(az group exists --name $RESOURCE_GROUP_NAME)
+ if [ "$rg_exists" = "false" ]; then
+ echo "๐ฆ Resource group does not exist. Creating new resource group '$RESOURCE_GROUP_NAME' in location '$AZURE_LOCATION'..."
+ az group create --name $RESOURCE_GROUP_NAME --location $AZURE_LOCATION || { echo "โ Error creating resource group"; exit 1; }
+ echo "โ
Resource group '$RESOURCE_GROUP_NAME' created successfully."
+ else
+ echo "โ
Resource group '$RESOURCE_GROUP_NAME' already exists. Deploying to existing resource group."
+ fi
+ echo "RESOURCE_GROUP_NAME=$RESOURCE_GROUP_NAME" >> $GITHUB_OUTPUT
+ echo "RESOURCE_GROUP_NAME=$RESOURCE_GROUP_NAME" >> $GITHUB_ENV
+
+ - name: Generate Unique Solution Prefix
+ id: generate_solution_prefix
+ shell: bash
+ run: |
+ set -e
+ COMMON_PART="psldg"
+ TIMESTAMP=$(date +%s)
+ UPDATED_TIMESTAMP=$(echo $TIMESTAMP | tail -c 6)
+ UNIQUE_SOLUTION_PREFIX="${COMMON_PART}${UPDATED_TIMESTAMP}"
+ echo "SOLUTION_PREFIX=${UNIQUE_SOLUTION_PREFIX}" >> $GITHUB_ENV
+ echo "Generated SOLUTION_PREFIX: ${UNIQUE_SOLUTION_PREFIX}"
+
+ - name: Determine Docker Image Tag
+ id: determine_image_tag
+ shell: bash
+ env:
+ INPUT_DOCKER_IMAGE_TAG: ${{ inputs.docker_image_tag }}
+ run: |
+ if [[ "${{ env.BUILD_DOCKER_IMAGE }}" == "true" ]]; then
+ if [[ -n "$INPUT_DOCKER_IMAGE_TAG" ]]; then
+ IMAGE_TAG="$INPUT_DOCKER_IMAGE_TAG"
+ echo "๐ Using Docker image tag from build job: $IMAGE_TAG"
+ else
+ echo "โ Docker build job failed or was skipped, but BUILD_DOCKER_IMAGE is true"
+ exit 1
+ fi
+ else
+ echo "๐ท๏ธ Using existing Docker image based on branch..."
+ BRANCH_NAME="${{ env.BRANCH_NAME }}"
+ echo "Current branch: $BRANCH_NAME"
+
+ if [[ "$BRANCH_NAME" == "main" ]]; then
+ IMAGE_TAG="latest_v4"
+ elif [[ "$BRANCH_NAME" == "dev-v4" ]]; then
+ IMAGE_TAG="dev_v4"
+ elif [[ "$BRANCH_NAME" == "hotfix" ]]; then
+ IMAGE_TAG="hotfix"
+ else
+ IMAGE_TAG="latest_v4"
+ fi
+ echo "Using existing Docker image tag: $IMAGE_TAG"
+ fi
+
+ echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
+ echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_OUTPUT
+
+ - name: Generate Unique Environment Name
+ id: generate_env_name
+ shell: bash
+ run: |
+ COMMON_PART="pslc"
+ TIMESTAMP=$(date +%s)
+ UPDATED_TIMESTAMP=$(echo $TIMESTAMP | tail -c 6)
+ UNIQUE_ENV_NAME="${COMMON_PART}${UPDATED_TIMESTAMP}"
+ echo "ENV_NAME=${UNIQUE_ENV_NAME}" >> $GITHUB_ENV
+ echo "Generated Environment Name: ${UNIQUE_ENV_NAME}"
+ echo "ENV_NAME=${UNIQUE_ENV_NAME}" >> $GITHUB_OUTPUT
+
+ - name: Display Workflow Configuration to GitHub Summary
+ shell: bash
+ env:
+ INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }}
+ INPUT_RUNNER_OS: ${{ inputs.runner_os }}
+ INPUT_AZURE_LOCATION: ${{ inputs.azure_location }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }}
+ EVENT_NAME: ${{ github.event_name }}
+ run: |
+ echo "## ๐ Workflow Configuration Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Configuration | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|---------------|-------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Trigger Type** | \`$EVENT_NAME\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Branch** | \`${{ env.BRANCH_NAME }}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Runner OS** | \`$INPUT_RUNNER_OS\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **WAF Enabled** | ${{ env.WAF_ENABLED == 'true' && 'โ
Yes' || 'โ No' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **EXP Enabled** | ${{ steps.configure_exp.outputs.EXP_ENABLED == 'true' && 'โ
Yes' || 'โ No' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Run E2E Tests** | \`${{ env.RUN_E2E_TESTS }}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Cleanup Resources** | ${{ env.CLEANUP_RESOURCES == 'true' && 'โ
Yes' || 'โ No' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Build Docker Image** | ${{ env.BUILD_DOCKER_IMAGE == 'true' && 'โ
Yes' || 'โ No' }} |" >> $GITHUB_STEP_SUMMARY
+
+ if [[ "$INPUT_TRIGGER_TYPE" == "workflow_dispatch" && -n "$INPUT_AZURE_LOCATION" ]]; then
+ echo "| **Azure Location** | \`$INPUT_AZURE_LOCATION\` (User Selected) |" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then
+ echo "| **Resource Group** | \`$INPUT_RESOURCE_GROUP_NAME\` (Pre-specified) |" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "| **Resource Group** | \`${{ env.RESOURCE_GROUP_NAME }}\` (Auto-generated) |" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ if [[ "$INPUT_TRIGGER_TYPE" != "workflow_dispatch" ]]; then
+ echo "โน๏ธ **Note:** Automatic Trigger - Using Non-WAF + Non-EXP configuration" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "โน๏ธ **Note:** Manual Trigger - Using user-specified configuration" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ deploy-linux:
+ name: Deploy on Linux
+ needs: azure-setup
+ if: inputs.runner_os == 'ubuntu-latest' && !cancelled() && needs.azure-setup.result == 'success'
+ uses: ./.github/workflows/job-deploy-linux.yml
+ with:
+ ENV_NAME: ${{ needs.azure-setup.outputs.ENV_NAME }}
+ AZURE_ENV_OPENAI_LOCATION: ${{ needs.azure-setup.outputs.AZURE_ENV_OPENAI_LOCATION }}
+ AZURE_LOCATION: ${{ needs.azure-setup.outputs.AZURE_LOCATION }}
+ RESOURCE_GROUP_NAME: ${{ needs.azure-setup.outputs.RESOURCE_GROUP_NAME }}
+ IMAGE_TAG: ${{ needs.azure-setup.outputs.IMAGE_TAG }}
+ BUILD_DOCKER_IMAGE: ${{ inputs.build_docker_image || 'false' }}
+ EXP: ${{ needs.azure-setup.outputs.EXP_ENABLED }}
+ WAF_ENABLED: ${{ inputs.waf_enabled == true && 'true' || 'false' }}
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ secrets: inherit
+
+ deploy-windows:
+ name: Deploy on Windows
+ needs: azure-setup
+ if: inputs.runner_os == 'windows-latest' && !cancelled() && needs.azure-setup.result == 'success'
+ uses: ./.github/workflows/job-deploy-windows.yml
+ with:
+ ENV_NAME: ${{ needs.azure-setup.outputs.ENV_NAME }}
+ AZURE_ENV_OPENAI_LOCATION: ${{ needs.azure-setup.outputs.AZURE_ENV_OPENAI_LOCATION }}
+ AZURE_LOCATION: ${{ needs.azure-setup.outputs.AZURE_LOCATION }}
+ RESOURCE_GROUP_NAME: ${{ needs.azure-setup.outputs.RESOURCE_GROUP_NAME }}
+ IMAGE_TAG: ${{ needs.azure-setup.outputs.IMAGE_TAG }}
+ BUILD_DOCKER_IMAGE: ${{ inputs.build_docker_image || 'false' }}
+ EXP: ${{ needs.azure-setup.outputs.EXP_ENABLED }}
+ WAF_ENABLED: ${{ inputs.waf_enabled == true && 'true' || 'false' }}
+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
+ secrets: inherit
diff --git a/.github/workflows/job-docker-build.yml b/.github/workflows/job-docker-build.yml
new file mode 100644
index 000000000..863b783e2
--- /dev/null
+++ b/.github/workflows/job-docker-build.yml
@@ -0,0 +1,125 @@
+name: Docker Build Job
+on:
+ workflow_call:
+ inputs:
+ trigger_type:
+ description: 'Trigger type (workflow_dispatch, pull_request, schedule)'
+ required: true
+ type: string
+ build_docker_image:
+ description: 'Build And Push Docker Image (Optional)'
+ required: false
+ default: false
+ type: boolean
+ outputs:
+ IMAGE_TAG:
+ description: "Generated Docker Image Tag"
+ value: ${{ jobs.docker-build.outputs.IMAGE_TAG }}
+
+env:
+ BRANCH_NAME: ${{ github.event.workflow_run.head_branch || github.head_ref || github.ref_name }}
+
+jobs:
+ docker-build:
+ if: inputs.trigger_type == 'workflow_dispatch' && inputs.build_docker_image == true
+ runs-on: ubuntu-latest
+ environment: production
+ outputs:
+ IMAGE_TAG: ${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Generate Unique Docker Image Tag
+ id: generate_docker_tag
+ shell: bash
+ run: |
+ echo "๐จ Building new Docker image - generating unique tag..."
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
+ RUN_ID="${{ github.run_id }}"
+ BRANCH_NAME="${{ github.head_ref || github.ref_name }}"
+ CLEAN_BRANCH_NAME=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9._-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')
+ UNIQUE_TAG="${CLEAN_BRANCH_NAME}-${TIMESTAMP}-${RUN_ID}"
+ echo "IMAGE_TAG=$UNIQUE_TAG" >> $GITHUB_ENV
+ echo "IMAGE_TAG=$UNIQUE_TAG" >> $GITHUB_OUTPUT
+ echo "Generated unique Docker tag: $UNIQUE_TAG"
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Log in to Azure Container Registry
+ run: az acr login --name ${{ secrets.ACR_TEST_LOGIN_SERVER }}
+
+ - name: Build and optionally push Backend Docker image
+ uses: docker/build-push-action@v6
+ env:
+ DOCKER_BUILD_SUMMARY: false
+ with:
+ context: ./src/backend
+ file: ./src/backend/Dockerfile
+ push: true
+ tags: |
+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/macaebackend:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/macaebackend:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}_${{ github.run_number }}
+
+ - name: Build and optionally push Frontend Docker image
+ uses: docker/build-push-action@v6
+ env:
+ DOCKER_BUILD_SUMMARY: false
+ with:
+ context: ./src/App
+ file: ./src/App/Dockerfile
+ push: true
+ tags: |
+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/macaefrontend:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/macaefrontend:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}_${{ github.run_number }}
+ - name: Build and optionally push MCP Docker image
+ uses: docker/build-push-action@v6
+ env:
+ DOCKER_BUILD_SUMMARY: false
+ with:
+ context: ./src/mcp_server
+ file: ./src/mcp_server/Dockerfile
+ push: true
+ tags: |
+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/macaemcp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/macaemcp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}_${{ github.run_number }}
+ - name: Verify Docker Image Build
+ shell: bash
+ run: |
+ echo "โ
Docker image successfully built and pushed"
+ echo "Image tag: ${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}"
+
+ - name: Generate Docker Build Summary
+ if: always()
+ shell: bash
+ run: |
+ ACR_NAME=$(echo "${{ secrets.ACR_TEST_LOGIN_SERVER }}")
+ echo "## ๐ณ Docker Build Job Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Job Status** | ${{ job.status == 'success' && 'โ
Success' || 'โ Failed' }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Image Tag** | \`${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Branch** | ${{ env.BRANCH_NAME }} |" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [[ "${{ job.status }}" == "success" ]]; then
+ echo "### โ
Build Details" >> $GITHUB_STEP_SUMMARY
+ echo "Successfully built and pushed three Docker images to ACR:" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "**Built Images:**" >> $GITHUB_STEP_SUMMARY
+ echo "- \`${ACR_NAME}/macaebackend:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}\`" >> $GITHUB_STEP_SUMMARY
+ echo "- \`${ACR_NAME}/macaefrontend:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}\`" >> $GITHUB_STEP_SUMMARY
+ echo "- \`${ACR_NAME}/macaemcp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}\`" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "### โ Build Failed" >> $GITHUB_STEP_SUMMARY
+ echo "- Docker build process encountered an error" >> $GITHUB_STEP_SUMMARY
+ echo "- Check the docker-build job for detailed error information" >> $GITHUB_STEP_SUMMARY
+ fi
diff --git a/.github/workflows/job-send-notification.yml b/.github/workflows/job-send-notification.yml
new file mode 100644
index 000000000..836639e25
--- /dev/null
+++ b/.github/workflows/job-send-notification.yml
@@ -0,0 +1,312 @@
+name: Send Notification Job
+
+on:
+ workflow_call:
+ inputs:
+ trigger_type:
+ description: 'Trigger type (workflow_dispatch, pull_request, schedule)'
+ required: true
+ type: string
+ waf_enabled:
+ description: 'Enable WAF'
+ required: false
+ default: false
+ type: boolean
+ EXP:
+ description: 'Enable EXP'
+ required: false
+ default: false
+ type: boolean
+ run_e2e_tests:
+ description: 'Run End-to-End Tests'
+ required: false
+ default: 'GoldenPath-Testing'
+ type: string
+ existing_webapp_url:
+ description: 'Existing Container WebApp URL (Skips Deployment)'
+ required: false
+ default: ''
+ type: string
+ deploy_result:
+ description: 'Deploy job result (success, failure, skipped)'
+ required: true
+ type: string
+ e2e_test_result:
+ description: 'E2E test job result (success, failure, skipped)'
+ required: false
+ default: ''
+ type: string
+ CONTAINER_WEB_APPURL:
+ description: 'Container Web App URL'
+ required: false
+ default: ''
+ type: string
+ RESOURCE_GROUP_NAME:
+ description: 'Resource Group Name'
+ required: false
+ default: ''
+ type: string
+ QUOTA_FAILED:
+ description: 'Quota Check Failed Flag'
+ required: false
+ default: 'false'
+ type: string
+ TEST_SUCCESS:
+ description: 'Test Success Flag'
+ required: false
+ default: ''
+ type: string
+ TEST_REPORT_URL:
+ description: 'Test Report URL'
+ required: false
+ default: ''
+ type: string
+ cleanup_result:
+ description: 'Cleanup job result (success, failure, skipped)'
+ required: false
+ default: 'skipped'
+ type: string
+
+env:
+ GPT_MIN_CAPACITY: 100
+ BRANCH_NAME: ${{ github.event.workflow_run.head_branch || github.head_ref || github.ref_name }}
+ WAF_ENABLED: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.waf_enabled || false) || false }}
+ EXP: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.EXP || false) || false }}
+ RUN_E2E_TESTS: ${{ inputs.trigger_type == 'workflow_dispatch' && (inputs.run_e2e_tests || 'GoldenPath-Testing') || 'GoldenPath-Testing' }}
+
+jobs:
+ send-notification:
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ env:
+ accelerator_name: "MACAE V4"
+ steps:
+ - name: Determine Test Suite Display Name
+ id: test_suite
+ shell: bash
+ env:
+ RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }}
+ run: |
+ if [ "$RUN_E2E_TESTS" = "GoldenPath-Testing" ]; then
+ TEST_SUITE_NAME="Golden Path Testing"
+ elif [ "$RUN_E2E_TESTS" = "Smoke-Testing" ]; then
+ TEST_SUITE_NAME="Smoke Testing"
+ elif [ "$RUN_E2E_TESTS" = "None" ]; then
+ TEST_SUITE_NAME="None"
+ else
+ TEST_SUITE_NAME="$RUN_E2E_TESTS"
+ fi
+ echo "TEST_SUITE_NAME=$TEST_SUITE_NAME" >> $GITHUB_OUTPUT
+ echo "Test Suite: $TEST_SUITE_NAME"
+
+ - name: Determine Cleanup Status
+ id: cleanup
+ shell: bash
+ env:
+ CLEANUP_RESULT: ${{ inputs.cleanup_result }}
+ run: |
+ case "$CLEANUP_RESULT" in
+ success) echo "CLEANUP_STATUS=โ
SUCCESS" >> $GITHUB_OUTPUT ;;
+ failure) echo "CLEANUP_STATUS=โ FAILED (Needs Manual Cleanup)" >> $GITHUB_OUTPUT ;;
+ *) echo "CLEANUP_STATUS=โญ๏ธ SKIPPED (Needs Manual Cleanup)" >> $GITHUB_OUTPUT ;;
+ esac
+
+ - name: Determine Configuration Label
+ id: config
+ shell: bash
+ env:
+ WAF_ENABLED: ${{ env.WAF_ENABLED }}
+ EXP: ${{ env.EXP }}
+ run: |
+ WAF_LABEL=$( [ "$WAF_ENABLED" = "true" ] && echo "WAF" || echo "Non-WAF" )
+ EXP_LABEL=$( [ "$EXP" = "true" ] && echo "EXP" || echo "Non-EXP" )
+ echo "CONFIG_LABEL=${WAF_LABEL} + ${EXP_LABEL}" >> $GITHUB_OUTPUT
+
+ - name: Send Quota Failure Notification
+ if: inputs.deploy_result == 'failure' && inputs.QUOTA_FAILED == 'true'
+ shell: bash
+ env:
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
+ CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
+ run: |
+ RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+ EMAIL_BODY=$(cat <Dear Team,
We would like to inform you that the ${ACCELERATOR_NAME} deployment has failed due to insufficient quota.
Status Summary:
Stage Status Deployment โ FAILED (Insufficient Quota) E2E Tests โญ๏ธ SKIPPED Cleanup ${CLEANUP_STATUS}
Configuration: ${CONFIG_LABEL}
Run URL: ${RUN_URL}
Please resolve the quota issue and retry the deployment.
Best regards, Your Automation Team
",
+ "subject": "โ[CI/CD-Automation] [${ACCELERATOR_NAME}] Insufficient Quota"
+ }
+ EOF
+ )
+
+ curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d "$EMAIL_BODY" || echo "Failed to send quota failure notification"
+
+ - name: Send Deployment Failure Notification
+ if: inputs.deploy_result == 'failure' && inputs.QUOTA_FAILED != 'true'
+ shell: bash
+ env:
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
+ CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
+ run: |
+ RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
+ RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME"
+
+ EMAIL_BODY=$(cat <Dear Team,We would like to inform you that the ${ACCELERATOR_NAME} deployment has failed.
Status Summary:
Stage Status Deployment โ FAILED (Deployment Issue) E2E Tests โญ๏ธ SKIPPED Cleanup ${CLEANUP_STATUS}
Deployment Details: โข Resource Group: ${RESOURCE_GROUP}
Configuration: ${CONFIG_LABEL}
Run URL: ${RUN_URL}
Please investigate the deployment failure at your earliest convenience.
Best regards, Your Automation Team
",
+ "subject": "โ[CI/CD-Automation] [${ACCELERATOR_NAME}] Deployment-Failed"
+ }
+ EOF
+ )
+
+ curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d "$EMAIL_BODY" || echo "Failed to send deployment failure notification"
+
+ - name: Send Success Notification
+ if: inputs.deploy_result == 'success' && (inputs.e2e_test_result == 'skipped' || inputs.TEST_SUCCESS == 'true')
+ shell: bash
+ env:
+ INPUT_CONTAINER_WEB_APPURL: ${{ inputs.CONTAINER_WEB_APPURL }}
+ INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }}
+ INPUT_E2E_TEST_RESULT: ${{ inputs.e2e_test_result }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
+ CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
+ RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }}
+ TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }}
+
+ run: |
+ RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+ WEBAPP_URL="${INPUT_CONTAINER_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}"
+ RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME"
+ TEST_REPORT_URL="$INPUT_TEST_REPORT_URL"
+
+ if [ "$INPUT_E2E_TEST_RESULT" = "skipped" ]; then
+ EMAIL_BODY=$(cat <Dear Team,We would like to inform you that the ${ACCELERATOR_NAME} deployment has completed successfully.
Status Summary:
Stage Status Deployment โ
SUCCESS E2E Tests โญ๏ธ SKIPPED Cleanup ${CLEANUP_STATUS}
Deployment Details: โข Resource Group: ${RESOURCE_GROUP} โข Web App URL: ${WEBAPP_URL}
Configuration: ${CONFIG_LABEL}
Run URL: ${RUN_URL}
Best regards, Your Automation Team
",
+ "subject": "โ
[CI/CD-Automation] [${ACCELERATOR_NAME}] Success"
+ }
+ EOF
+ )
+ else
+ EMAIL_BODY=$(cat <Dear Team,We would like to inform you that the ${ACCELERATOR_NAME} deployment and test automation has completed successfully.
Status Summary:
Stage Status Deployment โ
SUCCESS E2E Tests โ
SUCCESS Cleanup ${CLEANUP_STATUS}
Deployment Details: โข Resource Group: ${RESOURCE_GROUP} โข Web App URL: ${WEBAPP_URL} โข Test Suite: ${TEST_SUITE_NAME} โข Test Report: View Report
Configuration: ${CONFIG_LABEL}
Run URL: ${RUN_URL}
Best regards, Your Automation Team
",
+ "subject": "โ
[CI/CD-Automation] [${ACCELERATOR_NAME}] Success"
+ }
+ EOF
+ )
+ fi
+
+ curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d "$EMAIL_BODY" || echo "Failed to send success notification"
+
+ - name: Send Test Failure Notification
+ if: inputs.deploy_result == 'success' && inputs.e2e_test_result != 'skipped' && inputs.TEST_SUCCESS != 'true'
+ shell: bash
+ env:
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ INPUT_CONTAINER_WEB_APPURL: ${{ inputs.CONTAINER_WEB_APPURL }}
+ INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
+ INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
+ CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
+ RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }}
+ TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }}
+ INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }}
+ run: |
+ RUN_URL="https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}"
+ TEST_REPORT_URL="$INPUT_TEST_REPORT_URL"
+ WEBAPP_URL="${INPUT_CONTAINER_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}"
+ RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME"
+
+ EMAIL_BODY=$(cat <Dear Team,We would like to inform you that ${ACCELERATOR_NAME} test automation has failed.
Status Summary:
Stage Status Deployment โ
SUCCESS E2E Tests โ FAILED Cleanup ${CLEANUP_STATUS}
Deployment Details: โข Resource Group: ${RESOURCE_GROUP} โข Web App URL: ${WEBAPP_URL} โข Test Suite: ${TEST_SUITE_NAME} โข Test Report: View Report
Configuration: ${CONFIG_LABEL}
Run URL: ${RUN_URL}
Please investigate the matter at your earliest convenience.
Best regards, Your Automation Team
",
+ "subject": "โ[CI/CD-Automation] [${ACCELERATOR_NAME}] E2E Test-Failed"
+ }
+ EOF
+ )
+
+ curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d "$EMAIL_BODY" || echo "Failed to send test failure notification"
+
+ - name: Send Existing URL Success Notification
+ if: inputs.deploy_result == 'skipped' && inputs.existing_webapp_url != '' && inputs.e2e_test_result == 'success' && (inputs.TEST_SUCCESS == 'true' || inputs.TEST_SUCCESS == '')
+ shell: bash
+ env:
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
+ INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
+ RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }}
+ TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }}
+ run: |
+ RUN_URL="https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}"
+ EXISTING_URL="$INPUT_EXISTING_WEBAPP_URL"
+ TEST_REPORT_URL="$INPUT_TEST_REPORT_URL"
+
+ EMAIL_BODY=$(cat <Dear Team,The ${ACCELERATOR_NAME} pipeline executed against the specified Target URL and test automation has completed successfully.
Status Summary:
Stage Status Deployment โญ๏ธ SKIPPED (Tests executed on Pre-deployed RG) E2E Tests โ
SUCCESS Cleanup ${CLEANUP_STATUS}
Test Results: โข Test Suite: ${TEST_SUITE_NAME} ${TEST_REPORT_URL:+โข Test Report: View Report } โข Target URL: ${EXISTING_URL}
Run URL: ${RUN_URL}
Best regards, Your Automation Team
",
+ "subject": "โ
[CI/CD-Automation] [${ACCELERATOR_NAME}] Success"
+ }
+ EOF
+ )
+
+ curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d "$EMAIL_BODY" || echo "Failed to send existing URL success notification"
+
+ - name: Send Existing URL Test Failure Notification
+ if: inputs.deploy_result == 'skipped' && inputs.existing_webapp_url != '' && inputs.e2e_test_result == 'failure'
+ shell: bash
+ env:
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
+ INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
+ RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }}
+ TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }}
+ run: |
+ RUN_URL="https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}"
+ EXISTING_URL="$INPUT_EXISTING_WEBAPP_URL"
+ TEST_REPORT_URL="$INPUT_TEST_REPORT_URL"
+
+ EMAIL_BODY=$(cat <Dear Team,The ${ACCELERATOR_NAME} pipeline executed against the specified Target URL and test automation has failed.
Status Summary:
Stage Status Deployment โญ๏ธ SKIPPED (Tests executed on Pre-deployed RG) E2E Tests โ FAILED Cleanup ${CLEANUP_STATUS}
Failure Details: โข Target URL: ${EXISTING_URL} ${TEST_REPORT_URL:+โข Test Report: View Report } โข Test Suite: ${TEST_SUITE_NAME}
Run URL: ${RUN_URL}
Best regards, Your Automation Team
",
+ "subject": "โ[CI/CD-Automation] [${ACCELERATOR_NAME}] E2E Test-Failed"
+ }
+ EOF
+ )
+
+ curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d "$EMAIL_BODY" || echo "Failed to send existing URL test failure notification"
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index bc2851159..aa973c5c7 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -1,6 +1,14 @@
name: PyLint
-on: [push]
+on:
+ push:
+ paths:
+ - 'src/backend/**/*.py'
+ - 'src/mcp_server/**/*.py'
+ - 'src/backend/requirements.txt'
+ - '.flake8'
+ - '.github/workflows/pylint.yml'
+
jobs:
build:
@@ -12,7 +20,7 @@ jobs:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
@@ -24,4 +32,4 @@ jobs:
- name: Run flake8 and pylint
run: |
- flake8 --config=.flake8 src/backend # Specify the directory to lint
+ flake8 --config=.flake8 src/backend
\ No newline at end of file
diff --git a/.github/workflows/test-automation-v2.yml b/.github/workflows/test-automation-v2.yml
new file mode 100644
index 000000000..394adbe58
--- /dev/null
+++ b/.github/workflows/test-automation-v2.yml
@@ -0,0 +1,196 @@
+name: Test Automation Macae v4
+
+on:
+ workflow_call:
+ inputs:
+ MACAE_WEB_URL:
+ required: false
+ type: string
+ description: "Web URL for MACAE (overrides environment variable)"
+ MACAE_URL_API:
+ required: false
+ type: string
+ description: "API URL for MACAE (overrides environment variable)"
+ MACAE_RG:
+ required: false
+ type: string
+ TEST_SUITE:
+ required: false
+ type: string
+ default: "GoldenPath-Testing"
+ description: "Test suite to run: 'Smoke-Testing', 'GoldenPath-Testing' "
+ outputs:
+ TEST_SUCCESS:
+ description: "Whether tests passed"
+ value: ${{ jobs.test.outputs.TEST_SUCCESS }}
+ TEST_REPORT_URL:
+ description: "URL to test report artifact"
+ value: ${{ jobs.test.outputs.TEST_REPORT_URL }}
+
+env:
+ MACAE_WEB_URL: ${{ inputs.MACAE_WEB_URL }}
+ MACAE_URL_API: ${{ inputs.MACAE_URL_API }}
+ MACAE_RG: ${{ inputs.MACAE_RG }}
+ TEST_SUITE: ${{ inputs.TEST_SUITE }}
+ accelerator_name: "MACAE v4"
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ environment: production
+ outputs:
+ TEST_SUCCESS: ${{ steps.test1.outcome == 'success' || steps.test2.outcome == 'success' || steps.test3.outcome == 'success' }}
+ TEST_REPORT_URL: ${{ steps.upload_report.outputs.artifact-url }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.13'
+
+ - name: Login to Azure
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r tests/e2e-test/requirements.txt
+
+ - name: Ensure browsers are installed
+ run: python -m playwright install --with-deps chromium
+
+ - name: Validate Inputs
+ run: |
+ if [ -z "${{ env.MACAE_WEB_URL }}" ]; then
+ echo "ERROR: No Web URL provided for testing"
+ exit 1
+ fi
+
+ - name: Wait for Application to be Ready
+ run: |
+ echo "Waiting for application to be ready at ${{ env.MACAE_WEB_URL }}"
+ max_attempts=10
+ attempt=1
+ while [ $attempt -le $max_attempts ]; do
+ echo "Attempt $attempt: Checking if application is ready..."
+ if curl -f -s "${{ env.MACAE_WEB_URL }}" > /dev/null; then
+ echo "Application is ready!"
+ break
+ fi
+ if [ $attempt -eq $max_attempts ]; then
+ echo "Application is not ready after $max_attempts attempts"
+ exit 1
+ fi
+ echo "Application not ready, waiting 30 seconds..."
+ sleep 30
+ attempt=$((attempt + 1))
+ done
+
+ - name: Run tests(1)
+ id: test1
+ run: |
+ if [ "${{ env.TEST_SUITE }}" == "GoldenPath-Testing" ]; then
+ xvfb-run pytest -m gp --html=report/report.html --self-contained-html
+ else
+ xvfb-run pytest --html=report/report.html --self-contained-html
+ fi
+ working-directory: tests/e2e-test
+ continue-on-error: true
+
+ - name: Sleep for 30 seconds
+ if: ${{ steps.test1.outcome == 'failure' }}
+ run: sleep 30s
+ shell: bash
+
+ - name: Run tests(2)
+ id: test2
+ if: ${{ steps.test1.outcome == 'failure' }}
+ run: |
+ if [ "${{ env.TEST_SUITE }}" == "GoldenPath-Testing" ]; then
+ xvfb-run pytest -m gp --html=report/report.html --self-contained-html
+ else
+ xvfb-run pytest --html=report/report.html --self-contained-html
+ fi
+ working-directory: tests/e2e-test
+ continue-on-error: true
+
+ - name: Sleep for 60 seconds
+ if: ${{ steps.test2.outcome == 'failure' }}
+ run: sleep 60s
+ shell: bash
+
+ - name: Run tests(3)
+ id: test3
+ if: ${{ steps.test2.outcome == 'failure' }}
+ run: |
+ if [ "${{ env.TEST_SUITE }}" == "GoldenPath-Testing" ]; then
+ xvfb-run pytest -m gp --html=report/report.html --self-contained-html
+ else
+ xvfb-run pytest --html=report/report.html --self-contained-html
+ fi
+ working-directory: tests/e2e-test
+
+ - name: Upload test report
+ id: upload_report
+ uses: actions/upload-artifact@v4
+ if: ${{ !cancelled() }}
+ with:
+ name: test-report
+ path: tests/e2e-test/report/*
+
+ - name: Generate E2E Test Summary
+ if: always()
+ run: |
+ # Determine test suite type for title
+ if [ "${{ env.TEST_SUITE }}" == "GoldenPath-Testing" ]; then
+ echo "## ๐งช E2E Test Job Summary : Golden Path Testing" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "## ๐งช E2E Test Job Summary : Smoke Testing" >> $GITHUB_STEP_SUMMARY
+ fi
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
+
+ # Determine overall test result
+ OVERALL_SUCCESS="${{ steps.test1.outcome == 'success' || steps.test2.outcome == 'success' || steps.test3.outcome == 'success' }}"
+ if [[ "$OVERALL_SUCCESS" == "true" ]]; then
+ echo "| **Job Status** | โ
Success |" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "| **Job Status** | โ Failed |" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ echo "| **Target URL** | [${{ env.MACAE_WEB_URL }}](${{ env.MACAE_WEB_URL }}) |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Test Suite** | \`${{ env.TEST_SUITE }}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Test Report** | [Download Artifact](${{ steps.upload_report.outputs.artifact-url }}) |" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ echo "### ๐ Test Execution Details" >> $GITHUB_STEP_SUMMARY
+ echo "| Attempt | Status | Notes |" >> $GITHUB_STEP_SUMMARY
+ echo "|---------|--------|-------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Test Run 1** | ${{ steps.test1.outcome == 'success' && 'โ
Passed' || 'โ Failed' }} | Initial test execution |" >> $GITHUB_STEP_SUMMARY
+
+ if [[ "${{ steps.test1.outcome }}" == "failure" ]]; then
+ echo "| **Test Run 2** | ${{ steps.test2.outcome == 'success' && 'โ
Passed' || steps.test2.outcome == 'failure' && 'โ Failed' || 'โธ๏ธ Skipped' }} | Retry after 30s delay |" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ if [[ "${{ steps.test2.outcome }}" == "failure" ]]; then
+ echo "| **Test Run 3** | ${{ steps.test3.outcome == 'success' && 'โ
Passed' || steps.test3.outcome == 'failure' && 'โ Failed' || 'โธ๏ธ Skipped' }} | Final retry after 60s delay |" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ if [[ "$OVERALL_SUCCESS" == "true" ]]; then
+ echo "### โ
Test Results" >> $GITHUB_STEP_SUMMARY
+ echo "- End-to-end tests completed successfully" >> $GITHUB_STEP_SUMMARY
+ echo "- Application is functioning as expected" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "### โ Test Results" >> $GITHUB_STEP_SUMMARY
+ echo "- All test attempts failed" >> $GITHUB_STEP_SUMMARY
+ echo "- Check the e2e-test/test job for detailed error information" >> $GITHUB_STEP_SUMMARY
+ fi
\ No newline at end of file
diff --git a/.github/workflows/test-automation.yml b/.github/workflows/test-automation.yml
index 6c4344739..4a893c4c7 100644
--- a/.github/workflows/test-automation.yml
+++ b/.github/workflows/test-automation.yml
@@ -1,5 +1,9 @@
name: Test Automation MACAE
+permissions:
+ id-token: write
+ contents: read
+
on:
workflow_dispatch:
workflow_call:
@@ -26,12 +30,13 @@ on:
jobs:
test:
runs-on: ubuntu-latest
+ environment: production
env:
MACAE_WEB_URL: ${{ inputs.MACAE_WEB_URL }}
MACAE_URL_API: ${{ inputs.MACAE_URL_API }}
MACAE_RG: ${{ inputs.MACAE_RG }}
MACAE_CONTAINER_APP: ${{ inputs.MACAE_CONTAINER_APP }}
- accelerator_name: "MACAE v3"
+ accelerator_name: "MACAE v4"
steps:
- name: Checkout repository
@@ -45,7 +50,9 @@ jobs:
- name: Azure CLI Login
uses: azure/login@v2
with:
- creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# - name: Start Container App
# uses: azure/cli@v2
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7d8ca7c0b..d85de9a37 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -4,9 +4,17 @@ on:
push:
branches:
- main
- - dev
- - demo
- - hotfix
+ - demo-v4
+ - dev-v4
+ paths:
+ - 'src/backend/**/*.py'
+ - 'src/tests/**/*.py'
+ - 'src/mcp_server/**/*.py'
+ - 'src/**/pyproject.toml'
+ - 'pytest.ini'
+ - 'conftest.py'
+ - 'src/backend/requirements.txt'
+ - '.github/workflows/test.yml'
pull_request:
types:
- opened
@@ -15,10 +23,17 @@ on:
- synchronize
branches:
- main
- - main
- - dev
- - demo
- - hotfix
+ - demo-v4
+ - dev-v4
+ paths:
+ - 'src/backend/**/*.py'
+ - 'src/tests/**/*.py'
+ - 'src/mcp_server/**/*.py'
+ - 'pytest.ini'
+ - 'conftest.py'
+ - 'src/backend/requirements.txt'
+ - 'src/**/pyproject.toml'
+ - '.github/workflows/test.yml'
jobs:
test:
@@ -26,7 +41,7 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
@@ -51,25 +66,31 @@ jobs:
- name: Run tests with coverage
if: env.skip_tests == 'false'
+ env:
+ PYTHONPATH: src:src/backend
run: |
- pytest --cov=. --cov-report=term-missing --cov-report=xml \
- --ignore=tests/e2e-test/tests \
- --ignore=src/backend/tests/test_app.py \
- --ignore=src/tests/agents/test_foundry_integration.py \
- --ignore=src/tests/mcp_server/test_factory.py \
- --ignore=src/tests/mcp_server/test_hr_service.py \
- --ignore=src/backend/tests/test_config.py \
- --ignore=src/tests/agents/test_human_approval_manager.py \
- --ignore=src/backend/tests/test_team_specific_methods.py \
- --ignore=src/backend/tests/models/test_messages.py \
- --ignore=src/backend/tests/test_otlp_tracing.py \
- --ignore=src/backend/tests/auth/test_auth_utils.py
-
- # - name: Run tests with coverage
- # if: env.skip_tests == 'false'
- # run: |
- # pytest --cov=. --cov-report=term-missing --cov-report=xml --ignore=tests/e2e-test/tests
+ # Run test_app.py first (isolation required)
+ python -m pytest src/tests/backend/test_app.py --cov=src/backend --cov-config=.coveragerc -q
+ # Run remaining backend tests with coverage append
+ python -m pytest src/tests/backend --cov=src/backend --cov-append --cov-report=term --cov-report=xml --cov-config=.coveragerc --ignore=src/tests/backend/test_app.py
+
+ - name: Check coverage threshold
+ if: env.skip_tests == 'false'
+ run: |
+ if [ -f coverage.xml ]; then
+ COVERAGE=$(python -c "import xml.etree.ElementTree as ET; tree = ET.parse('coverage.xml'); root = tree.getroot(); print(float(root.attrib.get('line-rate', 0)) * 100)")
+ echo "Overall coverage: $COVERAGE%"
+ if (( $(echo "$COVERAGE < 80" | bc -l) )); then
+ echo "::error::Coverage is below 80% threshold. Current: $COVERAGE%"
+ exit 1
+ fi
+ echo "โ
Coverage threshold met: $COVERAGE% >= 80%"
+ else
+ echo "::error::coverage.xml not found"
+ exit 1
+ fi
+
- name: Skip coverage report if no tests
if: env.skip_tests == 'true'
run: |
diff --git a/.github/workflows/validate-bicep-params.yml b/.github/workflows/validate-bicep-params.yml
new file mode 100644
index 000000000..a483c8473
--- /dev/null
+++ b/.github/workflows/validate-bicep-params.yml
@@ -0,0 +1,108 @@
+name: Validate Bicep Parameters
+
+permissions:
+ contents: read
+
+on:
+ schedule:
+ - cron: '30 6 * * 3' # Wednesday 12:00 PM IST (6:30 AM UTC)
+ pull_request:
+ branches:
+ - main
+ - dev
+ paths:
+ - 'infra/**/*.bicep'
+ - 'infra/**/*.parameters.json'
+ - 'infra/scripts/validate_bicep_params.py'
+ workflow_dispatch:
+
+env:
+ accelerator_name: "MACAE"
+
+jobs:
+ validate:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Validate infra/ parameters
+ id: validate_infra
+ continue-on-error: true
+ run: |
+ set +e
+ python infra/scripts/validate_bicep_params.py --dir infra --strict --no-color --json-output infra_results.json 2>&1 | tee infra_output.txt
+ EXIT_CODE=${PIPESTATUS[0]}
+ set -e
+ echo "## Infra Param Validation" >> "$GITHUB_STEP_SUMMARY"
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
+ cat infra_output.txt >> "$GITHUB_STEP_SUMMARY"
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
+ exit $EXIT_CODE
+
+ - name: Set overall result
+ id: result
+ run: |
+ if [[ "${{ steps.validate_infra.outcome }}" == "failure" ]]; then
+ echo "status=failure" >> "$GITHUB_OUTPUT"
+ else
+ echo "status=success" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Upload validation results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: bicep-validation-results
+ path: |
+ infra_results.json
+ retention-days: 30
+
+ - name: Send schedule notification on failure
+ if: github.event_name == 'schedule' && steps.result.outputs.status == 'failure'
+ env:
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ run: |
+ RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+ INFRA_OUTPUT=$(sed 's/&/\&/g; s/\</g; s/>/\>/g' infra_output.txt)
+
+ jq -n \
+ --arg name "${ACCELERATOR_NAME}" \
+ --arg infra "$INFRA_OUTPUT" \
+ --arg url "$RUN_URL" \
+ '{subject: ("Bicep Parameter Validation Report - " + $name + " - Issues Detected"), body: ("Dear Team,
The scheduled Bicep Parameter Validation for " + $name + " has detected parameter mapping errors.
infra/ Results:
" + $infra + " Run URL: " + $url + "
Please fix the parameter mapping issues at your earliest convenience.
Best regards, Your Automation Team
")}' \
+ | curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d @- || echo "Failed to send notification"
+
+ - name: Send schedule notification on success
+ if: github.event_name == 'schedule' && steps.result.outputs.status == 'success'
+ env:
+ LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ ACCELERATOR_NAME: ${{ env.accelerator_name }}
+ run: |
+ RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+ INFRA_OUTPUT=$(sed 's/&/\&/g; s/\</g; s/>/\>/g' infra_output.txt)
+
+ jq -n \
+ --arg name "${ACCELERATOR_NAME}" \
+ --arg infra "$INFRA_OUTPUT" \
+ --arg url "$RUN_URL" \
+ '{subject: ("Bicep Parameter Validation Report - " + $name + " - Passed"), body: ("Dear Team,
The scheduled Bicep Parameter Validation for " + $name + " has completed successfully. All parameter mappings are valid.
infra/ Results:
" + $infra + " Run URL: " + $url + "
Best regards, Your Automation Team
")}' \
+ | curl -X POST "${LOGICAPP_URL}" \
+ -H "Content-Type: application/json" \
+ -d @- || echo "Failed to send notification"
+
+ - name: Fail if errors found
+ if: steps.result.outputs.status == 'failure'
+ run: exit 1
diff --git a/.gitignore b/.gitignore
index 49913cee2..e62f35001 100644
--- a/.gitignore
+++ b/.gitignore
@@ -337,7 +337,7 @@ PublishScripts/
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
+# NuGet v4's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
diff --git a/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator.code-workspace b/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator.code-workspace
index 1f5237069..73853d046 100644
--- a/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator.code-workspace
+++ b/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator.code-workspace
@@ -4,7 +4,7 @@
"path": "."
},
// {
- // "path": "./src/frontend"
+ // "path": "./src/App"
// },
// {
// "path": "./src/backend"
diff --git a/README.md b/README.md
index 311c4145e..078ed55d0 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ The solution leverages Azure OpenAI Service, Azure Container Apps, Azure Cosmos
### Additional resources
-[Semantic Kernel Documentation](https://learn.microsoft.com/en-us/semantic-kernel/)
+[Agent Framework Documentation](https://learn.microsoft.com/en-us/agent-framework//)
[Azure AI Foundry Documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/)
@@ -75,11 +75,13 @@ Follow the quick deploy steps on the deployment guide to deploy this solution to
[Click here to launch the deployment guide](./docs/DeploymentGuide.md)
-| [](https://codespaces.new/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) | [](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) |
-|---|---|
+| [](https://codespaces.new/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) | [](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) | [&message=Open&color=blue&logo=visualstudiocode&logoColor=white)](https://vscode.dev/azure/?vscode-azure-exp=foundry&agentPayload=eyJiYXNlVXJsIjogImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9taWNyb3NvZnQvTXVsdGktQWdlbnQtQ3VzdG9tLUF1dG9tYXRpb24tRW5naW5lLVNvbHV0aW9uLUFjY2VsZXJhdG9yL3JlZnMvaGVhZHMvbWFpbi9pbmZyYS92c2NvZGVfd2ViIiwgImluZGV4VXJsIjogIi9pbmRleC5qc29uIiwgInZhcmlhYmxlcyI6IHsiYWdlbnRJZCI6ICIiLCAiY29ubmVjdGlvblN0cmluZyI6ICIiLCAidGhyZWFkSWQiOiAiIiwgInVzZXJNZXNzYWdlIjogIiIsICJwbGF5Z3JvdW5kTmFtZSI6ICIiLCAibG9jYXRpb24iOiAiIiwgInN1YnNjcmlwdGlvbklkIjogIiIsICJyZXNvdXJjZUlkIjogIiIsICJwcm9qZWN0UmVzb3VyY2VJZCI6ICIiLCAiZW5kcG9pbnQiOiAiIn0sICJjb2RlUm91dGUiOiBbImFpLXByb2plY3RzLXNkayIsICJweXRob24iLCAiZGVmYXVsdC1henVyZS1hdXRoIiwgImVuZHBvaW50Il19) |
+|---|---|---|
+> **Note**: Some tenants may have additional security restrictions that run periodically and could impact the application (e.g., blocking public network access). If you experience issues or the application stops working, check if these restrictions are the cause. In such cases, consider deploying the WAF-supported version to ensure compliance. To configure, [Click here](./docs/DeploymentGuide.md#31-choose-deployment-type-optional).
+
> โ ๏ธ **Important: 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 you deploy the solution.
@@ -148,6 +150,20 @@ By using the *Multi-Agent Custom Automation Engine* solution accelerator, users
+### Use Case
+
+ Click to learn more about what use cases this solution provides
+
+| Use Case | Persona | Challenges | Summary/Approach |
+|----------|-----------|------------|------------------|
+| Product Marketing | Marketing Executive | Marketing release plans require input from multiple teams (engineering, design, compliance), which often leads to delays and misalignment. Traditional planning involves repetitive tasks like drafting timelines, assigning owners, and validating compliance, which are prone to mistakes. | Through an agentic approach the Multi-agent speeds up release planning by automating repetitive tasks and enhances collaboration with dynamic agent teams that adapt to campaign needs. |
+| Onboarding Employee | HR Manager | Traditional onboarding involves multiple disconnected stepsโHR paperwork, IT setup, compliance trainingโwhich often require manual coordination and lead to delays. Manual steps slow collaboration and increase overhead, reducing efficiency and employee experience. | Designed to streamline the complex process of bringing new hires into an organization using a modular, agentic architecture. |
+| Retail Remediation | Customer Success Manager | This approach is seeking a smarter way toย manage workflows andย ensure customer satisfaction.ย Challenges include difficulty engaging the right agents, inefficiencies from manual processes, fragmented data, and limited security controls. | Using the Multi-Agent Custom Automation Engine, the Customer Success Manager recruits intelligent agents to analyze satisfaction and recommend steps for remediation.ย The platformโs modular design and reasoning capabilities allow for analyzing dependencies, planning transitions, and reducing manual rework. |
+| RFP Reviewer | VP of Finance | Reviewing RFPs manually under tight deadlines is challenging. Compliance checks and risk assessments slow progress, leaving critical gaps and delaying decisions. | Multi-Agent RFP Reviewer is an intelligent platform addresses these issues by deploying AI agents to review RFPs, identify risks, recommend remediation, and execute actions seamlessly - transforming a labor-intensive process into a fast, accurate, and compliant workflow. |
+| Contract Compliance Reviewer | Compliance Counsel | Reviewing Contracts for compliance manually under tight deadlines is challenging. Compliance checks and risk assessments slow progress, leaving critical gaps and delaying decisions. | Multi-Agent Contract Compliance Reviewer is an intelligent platform addresses these issues by deploying AI agents to review Contracts, identify risks, recommend remediation, and execute actions seamlessly - transforming a labor-intensive process into a fast, accurate, and compliant workflow. |
+
+
+
@@ -180,6 +196,15 @@ Check out similar solution accelerators
+๐ก Want to get familiar with Microsoft's AI and Data Engineering best practices? Check out our playbooks to learn more
+
+| Playbook | Description |
+|:---|:---|
+| [AI playbook](https://learn.microsoft.com/en-us/ai/playbook/) | The Artificial Intelligence (AI) Playbook provides enterprise software engineers with solutions, capabilities, and code developed to solve real-world AI problems. |
+| [Data playbook](https://learn.microsoft.com/en-us/data-engineering/playbook/understanding-data-playbook) | The data playbook provides enterprise software engineers with solutions which contain code developed to solve real-world problems. Everything in the playbook is developed with, and validated by, some of Microsoft's largest and most influential customers and partners. |
+
+
+
## Provide feedback
Have questions, find a bug, or want to request a feature? [Submit a new issue](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator/issues) on this repo and we'll connect.
@@ -192,6 +217,17 @@ Please refer to [Transparency FAQ](./docs/TRANSPARENCY_FAQ.md) for responsible A
## Disclaimers
+This release is an artificial intelligence (AI) system that generates text based on user input. The text generated by this system may include ungrounded content, meaning that it is not verified by any reliable source or based on any factual data. The data included in this release is synthetic, meaning that it is artificially created by the system and may contain factual errors or inconsistencies. Users of this release are responsible for determining the accuracy, validity, and suitability of any content generated by the system for their intended purposes. Users should not rely on the system output as a source of truth or as a substitute for human judgment or expertise.
+
+This release only supports English language input and output. Users should not attempt to use the system with any other language or format. The system output may not be compatible with any translation tools or services, and may lose its meaning or coherence if translated.
+
+This release does not reflect the opinions, views, or values of Microsoft Corporation or any of its affiliates, subsidiaries, or partners. The system output is solely based on the system's own logic and algorithms, and does not represent any endorsement, recommendation, or advice from Microsoft or any other entity. Microsoft disclaims any liability or responsibility for any damages, losses, or harms arising from the use of this release or its output by any user or third party.
+
+This release does not provide any financial advice, legal advice and is not designed to replace the role of qualified client advisors in appropriately advising clients. Users should not use the system output for any financial decisions, legal guidance or transactions, and should consult with a professional financial advisor and or legal advisor as appropriate before taking any action based on the system output. Microsoft is not a financial institution or a fiduciary, and does not offer any financial products or services through this release or its output.
+
+This release is intended as a proof of concept only, and is not a finished or polished product. It is not intended for commercial use or distribution, and is subject to change or discontinuation without notice. Any planned deployment of this release or its output should include comprehensive testing and evaluation to ensure it is fit for purpose and meets the user's requirements and expectations. Microsoft does not guarantee the quality, performance, reliability, or availability of this release or its output, and does not provide any warranty or support for it.
+
+This Software requires the use of third-party components which are governed by separate proprietary or open-source licenses as identified below, and you must comply with the terms of each applicable license in order to use the Software. You acknowledge and agree that this license does not grant you a license or other right to use any such third-party proprietary or open-source components.
To the extent that the Software includes components or code used in or derived from Microsoft products or services, including without limitation Microsoft Azure Services (collectively, "Microsoft Products and Services"), you must also comply with the Product Terms applicable to such Microsoft Products and Services. You acknowledge and agree that the license governing the Software does not grant you a license or other right to use Microsoft Products and Services. Nothing in the license or this ReadMe file will serve to supersede, amend, terminate or modify any terms in the Product Terms for any Microsoft Products and Services.
diff --git a/TRANSPARENCY_FAQS.md b/TRANSPARENCY_FAQS.md
index 8eae97ccf..b7fd9593f 100644
--- a/TRANSPARENCY_FAQS.md
+++ b/TRANSPARENCY_FAQS.md
@@ -1,7 +1,16 @@
# Multi-Agent-Custom-Automation-Engine โ Solution Accelerator : Responsible AI FAQ
+## Important Notice
+
+This accelerator is **intended solely for accelerating proofs of concept (POCs)**.
+It is **not designed or recommended for direct production deployment**.
+
+### Best Practices
+- **Do not use this accelerator in production environments without thorough review and adaptation.**
+- **Ensure all implementation decisions consider security, scalability, and compliance requirements for production.
+
## What is the Multi Agent: Custom Automation Engine โ Solution Accelerator?
-Multi Agent: Custom Automation Engine โ Solution Accelerator is an open-source GitHub Repository that enables users to solve complex tasks using multiple agents. The accelerator is designed to be generic across business tasks. The user enters a task and a planning LLM formulates a plan to complete that task. The system then dynamically generates agents which can complete the task. The system also allows the user to create actions that agents can take (for example sending emails or scheduling orientation sessions for new employees). These actions are taken into account by the planner and dynamically created agents may be empowered to take these actions.
+Multi Agent: Custom Automation Engine โ Solution Accelerator is an open-source GitHub Repository that enables users to solve complex tasks using multiple agents. The accelerator is designed to be generic across business tasks. The user enters a task and a planning LLM formulates a plan to complete that task. The system then dynamically generates agents which can complete the task. The system also allows the user to create actions that agents can take (for example sending emails or scheduling orientation sessions for new employees, drafting a press release, customer retail remediation, reviewing contracts, reviewing proposals). These actions are taken into account by the planner and dynamically created agents may be empowered to take these actions.
## What can the Multi Agent: Custom Automation Engine โ Solution Accelerator do?
The solution accelerator is designed to replace and enhance enterprise workflows and processes with intelligent automation. Agents can specialize in various functions and work together to achieve an objective as specified by the user. The accelerator will integrate seamlessly with existing systems and is designed to scale according to the needs of the customer. The system allows users to review, reorder and approve steps generated in a plan, ensuring human oversight. The system uses function calling with LLMs to perform actions, users can approve or modify these actions.
diff --git a/azure.yaml b/azure.yaml
index c483ddedd..a3b344d94 100644
--- a/azure.yaml
+++ b/azure.yaml
@@ -3,34 +3,47 @@ name: multi-agent-custom-automation-engine-solution-accelerator
metadata:
template: multi-agent-custom-automation-engine-solution-accelerator@1.0
requiredVersions:
- azd: '>= 1.18.0'
+ azd: '>= 1.18.0 != 1.23.9'
+ bicep: '>= 0.33.0'
hooks:
postdeploy:
windows:
run: |
- Write-Host ""
- Write-Host "===============================================================" -ForegroundColor Yellow
- Write-Host " POST-DEPLOYMENT STEPS (PowerShell) " -ForegroundColor Green
- Write-Host "===============================================================" -ForegroundColor Yellow
- Write-Host ""
+ # Detect if running in Git Bash or similar Bash environment
+ if ($env:SHELL -like "*bash*" -or $env:MSYSTEM) {
+ # Running in Git Bash/MSYS2 environment
+ Write-Host ""
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host " POST-DEPLOYMENT STEPS (Bash)" -ForegroundColor Green
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host ""
- Write-Host " STEP 1: Upload Team Configurations to Cosmos DB" -ForegroundColor White
- Write-Host " ๐ Run the following command in PowerShell:" -ForegroundColor White
- Write-Host " infra\scripts\Upload-Team-Config.ps1" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host " Upload Team Configurations and index sample data" -ForegroundColor White
+ Write-Host " ๐ Run the following command in Bash:" -ForegroundColor White
+ Write-Host " bash infra/scripts/selecting_team_config_and_data.sh" -ForegroundColor Cyan
+ Write-Host ""
- Write-Host " STEP 2: Index Sample Data into Azure Search" -ForegroundColor White
- Write-Host " ๐ Run the following command in PowerShell:" -ForegroundColor White
- Write-Host " infra\scripts\Process-Sample-Data.ps1" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host "๐ Access your deployed Frontend application at:" -ForegroundColor Green
+ Write-Host " https://$env:webSiteDefaultHostname" -ForegroundColor Cyan
+ Write-Host ""
+ } else {
+ # Running in PowerShell
+ Write-Host ""
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host " POST-DEPLOYMENT STEP (PowerShell) " -ForegroundColor Green
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host ""
- Write-Host "๐ก Alternatively, you can run above BOTH steps together using:" -ForegroundColor Yellow
- Write-Host " infra\scripts\Team-Config-And-Data.ps1" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host " Upload Team Configurations and index sample data" -ForegroundColor White
+ Write-Host " ๐ Run the following command in PowerShell:" -ForegroundColor White
+ Write-Host " infra\scripts\Selecting-Team-Config-And-Data.ps1" -ForegroundColor Cyan
+ Write-Host ""
- Write-Host "๐ Access your deployed Frontend application at:" -ForegroundColor Green
- Write-Host " https://$env:webSiteDefaultHostname" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host "๐ Access your deployed Frontend application at:" -ForegroundColor Green
+ Write-Host " https://$env:webSiteDefaultHostname" -ForegroundColor Cyan
+ Write-Host ""
+ }
+
shell: pwsh
interactive: true
posix:
@@ -40,28 +53,18 @@ hooks:
Yellow='\033[1;33m'
NC='\033[0m'
- echo ""
- echo "${Yellow}==============================================================="
- echo "${Green} POST-DEPLOYMENT STEPS (Bash) "
- echo "${Yellow}===============================================================${NC}"
- echo ""
+ printf "\n"
- echo " STEP 1: Upload Team Configurations to Cosmos DB"
- echo " ๐ Run the following command in Bash:"
- echo " ${Blue}bash infra/scripts/upload_team_config.sh${NC}"
- echo ""
+ printf "${Yellow}===============================================================\n"
+ printf "${Green} POST-DEPLOYMENT STEPS (Bash)\n"
+ printf "${Yellow}===============================================================${NC}\n\n"
- echo " STEP 2: Index Sample Data into Azure Search"
- echo " ๐ Run the following command in Bash:"
- echo " ${Blue}bash infra/scripts/process_sample_data.sh${NC}"
- echo ""
+ printf "Upload Team Configurations and index sample data:\n"
+ printf " ๐ Run the following command in Bash:\n"
+ printf " ${Blue}bash infra/scripts/selecting_team_config_and_data.sh${NC}\n\n"
- echo "๐ก Alternatively, you can run above BOTH steps together using:"
- echo " ${Blue}bash infra/scripts/team_config_and_data.sh${NC}"
- echo ""
+ printf "๐ Access your deployed Frontend application at:\n"
+ printf " ${Blue}https://%s${NC}\n\n" "$webSiteDefaultHostname"
- echo "๐ Access your deployed Frontend application at:"
- echo " ${Blue}https://$webSiteDefaultHostname${NC}"
- echo ""
shell: sh
interactive: true
diff --git a/azure_custom.yaml b/azure_custom.yaml
index c6cfd8be8..a1c7a1aa4 100644
--- a/azure_custom.yaml
+++ b/azure_custom.yaml
@@ -3,7 +3,8 @@ name: multi-agent-custom-automation-engine-solution-accelerator
metadata:
template: multi-agent-custom-automation-engine-solution-accelerator@1.0
requiredVersions:
- azd: ">=1.15.0 !=1.17.1"
+ azd: '>= 1.18.0 != 1.23.9'
+ bicep: '>= 0.33.0'
services:
backend:
@@ -13,6 +14,7 @@ services:
docker:
path: ./Dockerfile.NoCache
image: backend
+ registry: ${AZURE_CONTAINER_REGISTRY_ENDPOINT}
remoteBuild: true
mcp:
@@ -21,10 +23,11 @@ services:
host: containerapp
docker:
image: mcp
+ registry: ${AZURE_CONTAINER_REGISTRY_ENDPOINT}
remoteBuild: true
frontend:
- project: ./src/frontend
+ project: ./src/App
language: py
host: appservice
dist: ./dist
@@ -45,29 +48,41 @@ hooks:
postdeploy:
windows:
run: |
- Write-Host ""
- Write-Host "===============================================================" -ForegroundColor Yellow
- Write-Host " POST-DEPLOYMENT STEPS (PowerShell) " -ForegroundColor Green
- Write-Host "===============================================================" -ForegroundColor Yellow
- Write-Host ""
+ # Detect if running in Git Bash or similar Bash environment
+ if ($env:SHELL -like "*bash*" -or $env:MSYSTEM) {
+ # Running in Git Bash/MSYS2 environment
+ Write-Host ""
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host " POST-DEPLOYMENT STEPS (Bash)" -ForegroundColor Green
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host ""
- Write-Host " STEP 1: Upload Team Configurations to Cosmos DB" -ForegroundColor White
- Write-Host " ๐ Run the following command in PowerShell:" -ForegroundColor White
- Write-Host " infra\scripts\Upload-Team-Config.ps1" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host " Upload Team Configurations and index sample data" -ForegroundColor White
+ Write-Host " ๐ Run the following command in Bash:" -ForegroundColor White
+ Write-Host " bash infra/scripts/selecting_team_config_and_data.sh" -ForegroundColor Cyan
+ Write-Host ""
- Write-Host " STEP 2: Index Sample Data into Azure Search" -ForegroundColor White
- Write-Host " ๐ Run the following command in PowerShell:" -ForegroundColor White
- Write-Host " infra\scripts\Process-Sample-Data.ps1" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host "๐ Access your deployed Frontend application at:" -ForegroundColor Green
+ Write-Host " https://$env:webSiteDefaultHostname" -ForegroundColor Cyan
+ Write-Host ""
+ } else {
+ # Running in PowerShell
+ Write-Host ""
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host " POST-DEPLOYMENT STEP (PowerShell) " -ForegroundColor Green
+ Write-Host "===============================================================" -ForegroundColor Yellow
+ Write-Host ""
- Write-Host "๐ก Alternatively, you can run above BOTH steps together using:" -ForegroundColor Yellow
- Write-Host " infra\scripts\Team-Config-And-Data.ps1" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host " Upload Team Configurations and index sample data" -ForegroundColor White
+ Write-Host " ๐ Run the following command in PowerShell:" -ForegroundColor White
+ Write-Host " infra\scripts\Selecting-Team-Config-And-Data.ps1" -ForegroundColor Cyan
+ Write-Host ""
- Write-Host "๐ Access your deployed Frontend application at:" -ForegroundColor Green
- Write-Host " https://$env:webSiteDefaultHostname" -ForegroundColor Cyan
- Write-Host ""
+ Write-Host "๐ Access your deployed Frontend application at:" -ForegroundColor Green
+ Write-Host " https://$env:webSiteDefaultHostname" -ForegroundColor Cyan
+ Write-Host ""
+ }
+
shell: pwsh
interactive: true
posix:
@@ -77,28 +92,17 @@ hooks:
Yellow='\033[1;33m'
NC='\033[0m'
- echo ""
- echo "${Yellow}==============================================================="
- echo "${Green} POST-DEPLOYMENT STEPS (Bash) "
- echo "${Yellow}===============================================================${NC}"
- echo ""
+ printf "\n"
- echo " STEP 1: Upload Team Configurations to Cosmos DB"
- echo " ๐ Run the following command in Bash:"
- echo " ${Blue}bash infra/scripts/upload_team_config.sh${NC}"
- echo ""
+ printf "${Yellow}===============================================================\n"
+ printf "${Green} POST-DEPLOYMENT STEPS (Bash)\n"
+ printf "${Yellow}===============================================================${NC}\n\n"
- echo " STEP 2: Index Sample Data into Azure Search"
- echo " ๐ Run the following command in Bash:"
- echo " ${Blue}bash infra/scripts/process_sample_data.sh${NC}"
- echo ""
+ printf "Upload Team Configurations and index sample data:\n"
+ printf " ๐ Run the following command in Bash:\n"
+ printf " ${Blue}bash infra/scripts/selecting_team_config_and_data.sh${NC}\n\n"
- echo "๐ก Alternatively, you can run above BOTH steps together using:"
- echo " ${Blue}bash infra/scripts/team_config_and_data.sh${NC}"
- echo ""
-
- echo "๐ Access your deployed Frontend application at:"
- echo " ${Blue}https://$webSiteDefaultHostname${NC}"
- echo ""
+ printf "๐ Access your deployed Frontend application at:\n"
+ printf " ${Blue}https://%s${NC}\n\n" "$webSiteDefaultHostname"
shell: sh
interactive: true
\ No newline at end of file
diff --git a/conftest.py b/conftest.py
index f4bc94a94..9b5f3abb2 100644
--- a/conftest.py
+++ b/conftest.py
@@ -7,9 +7,18 @@
import pytest
-# Add the agents path
-agents_path = Path(__file__).parent.parent.parent / "backend" / "v3" / "magentic_agents"
-sys.path.insert(0, str(agents_path))
+# Get the root directory of the project
+root_dir = Path(__file__).parent
+
+# Add src directory to path for 'backend', 'common', 'v4' etc. imports
+src_path = root_dir / "src"
+if str(src_path) not in sys.path:
+ sys.path.insert(0, str(src_path))
+
+# Add src/backend to path for relative imports within backend
+backend_path = root_dir / "src" / "backend"
+if str(backend_path) not in sys.path:
+ sys.path.insert(0, str(backend_path))
@pytest.fixture
def agent_env_vars():
diff --git a/data/agent_teams/contract_compliance_team.json b/data/agent_teams/contract_compliance_team.json
new file mode 100644
index 000000000..6eac65be7
--- /dev/null
+++ b/data/agent_teams/contract_compliance_team.json
@@ -0,0 +1,70 @@
+{
+ "id": "1",
+ "team_id": "team-compliance-1",
+ "name": "Contract Compliance Review Team",
+ "status": "visible",
+ "created": "",
+ "created_by": "",
+ "deployment_name": "gpt-4.1-mini",
+ "description": "A multi-agent compliance review team that summarizes NDAs, identifies risks, checks compliance, and recommends improvements using advanced legal reasoning and retrieval-augmented analysis.",
+ "logo": "",
+ "plan": "",
+ "agents": [
+ {
+ "input_key": "",
+ "type": "summary",
+ "name": "ContractSummaryAgent",
+ "deployment_name": "gpt-4.1-mini",
+ "icon": "",
+ "system_message": "You are the Summary Agent for compliance contract analysis. Your task is to produce a clear, accurate, and structured executive summary of NDA and legal agreement documents. You must deliver summaries organized into labeled sections including: Overview, Parties, Effective Date, Purpose, Definition of Confidential Information, Receiving Party Obligations, Term & Termination, Governing Law, Restrictions & Limitations, Miscellaneous Clauses, Notable or Unusual Terms, and Key Items for Risk & Compliance Agents. Highlight missing elements such as liability caps, dispute resolution mechanisms, data handling obligations, or ambiguous language. Maintain a precise, neutral legal tone. Do not give legal opinions or risk assessmentsโonly summarize the content as written. Use retrieval results from the search index to ensure completeness and reference contextual definitions or standard clause expectations when needed.",
+ "description": "Produces comprehensive, structured summaries of NDAs and contracts, capturing all key terms, clauses, obligations, jurisdictions, and notable provisions.",
+ "use_rag": true,
+ "use_mcp": false,
+ "use_bing": false,
+ "use_reasoning": false,
+ "index_name": "contract-summary-doc-index",
+ "coding_tools": false
+ },
+ {
+ "input_key": "",
+ "type": "risk",
+ "name": "ContractRiskAgent",
+ "deployment_name": "gpt-4.1-mini",
+ "icon": "",
+ "system_message": "You are the Risk Agent for NDA and compliance contract analysis. Use the NDA Risk Assessment Reference document and retrieved context to identify High, Medium, and Low risk issues. Evaluate clauses for missing liability caps, ambiguous terms, overly broad confidentiality definitions, jurisdiction misalignment, missing termination rights, unclear data handling obligations, missing dispute resolution, and any incomplete or poorly scoped definitions. For every risk you identify, provide: (1) Risk Category (High/Medium/Low), (2) Clause or Section impacted, (3) Description of the issue, (4) Why it matters or what exposure it creates, and (5) Suggested edit or corrective language. Apply the risk scoring framework: High = escalate immediately; Medium = requires revision; Low = minor issue. Be precise, legally aligned, and practical. Reference retrieved examples or standards when appropriate. Your output must be structured and actionable.",
+ "description": "Identifies and classifies compliance risks in NDAs and contracts using the organization's risk framework, and provides suggested edits to reduce exposure.",
+ "use_rag": true,
+ "use_mcp": false,
+ "use_bing": false,
+ "use_reasoning": false,
+ "index_name": "contract-risk-doc-index",
+ "coding_tools": false
+ },
+ {
+ "input_key": "",
+ "type": "compliance",
+ "name": "ContractComplianceAgent",
+ "deployment_name": "gpt-4.1-mini",
+ "icon": "",
+ "system_message": "You are the Compliance Agent responsible for validating NDAs and legal agreements against mandatory legal and policy requirements. Use the NDA Compliance Reference Document and retrieval results to evaluate whether the contract includes all required clauses: Confidentiality, Term & Termination, Governing Law aligned to approved jurisdictions, Non-Assignment, and Entire Agreement. Identify compliance gaps including ambiguous language, missing liability protections, improper jurisdiction, excessive term length, insufficient data protection obligations, missing dispute resolution mechanisms, or export control risks. For each issue provide: (1) Compliance Area (e.g., Term Length, Jurisdiction, Confidentiality), (2) Status (Pass/Fail), (3) Issue Description, (4) Whether it is Mandatory or Recommended, (5) Corrective Recommendation or Suggested Language. Deliver a final Compliance Status summary. Maintain professional, objective, legally accurate tone.",
+ "description": "Performs compliance validation of NDAs and contracts against legal policy requirements, identifies gaps, and provides corrective recommendations and compliance status.",
+ "use_rag": true,
+ "use_mcp": false,
+ "use_bing": false,
+ "use_reasoning": false,
+ "index_name": "contract-compliance-doc-index",
+ "coding_tools": false
+ }
+ ],
+ "protected": false,
+ "starting_tasks": [
+ {
+ "id": "task-1",
+ "name": "NDA Contract Review",
+ "prompt": "Review Contoso's NDA. Provide a summary (parties, date, term, governing law), assess risks (High/Medium/Low with clause references), audit compliance against company policy, and suggest edits for any issues.",
+ "created": "",
+ "creator": "",
+ "logo": ""
+ }
+ ]
+}
diff --git a/data/agent_teams/hr.json b/data/agent_teams/hr.json
index eac1de2e0..54d618deb 100644
--- a/data/agent_teams/hr.json
+++ b/data/agent_teams/hr.json
@@ -5,6 +5,7 @@
"status": "visible",
"created": "",
"created_by": "",
+ "deployment_name": "gpt-4.1-mini",
"agents": [
{
"input_key": "",
diff --git a/data/agent_teams/marketing.json b/data/agent_teams/marketing.json
index a6de2ec5f..ad04f87b1 100644
--- a/data/agent_teams/marketing.json
+++ b/data/agent_teams/marketing.json
@@ -5,6 +5,7 @@
"status": "visible",
"created": "",
"created_by": "",
+ "deployment_name": "gpt-4.1-mini",
"agents": [
{
"input_key": "",
diff --git a/data/agent_teams/retail.json b/data/agent_teams/retail.json
index 66a5f4c74..2f3f3a0b5 100644
--- a/data/agent_teams/retail.json
+++ b/data/agent_teams/retail.json
@@ -5,6 +5,7 @@
"status": "visible",
"created": "",
"created_by": "",
+ "deployment_name": "gpt-4.1-mini",
"agents": [
{
"input_key": "",
@@ -18,9 +19,7 @@
"use_mcp": false,
"use_bing": false,
"use_reasoning": false,
- "index_name": "macae-index",
- "index_foundry_name": "",
- "index_endpoint": "",
+ "index_name": "macae-retail-customer-index",
"coding_tools": false
},
{
@@ -35,7 +34,7 @@
"use_mcp": false,
"use_bing": false,
"use_reasoning": false,
- "index_name": "macae-index",
+ "index_name": "macae-retail-order-index",
"index_foundry_name": "",
"coding_tools": false
},
@@ -45,8 +44,8 @@
"name": "AnalysisRecommendationAgent",
"deployment_name": "o4-mini",
"icon": "",
- "system_message": "You are a reasoning agent that can analyze customer data and provide recommendations for improving customer satisfaction and retention. You do not have access to any data sources, but you can reason based on the information provided to you by other agents. Use your reasoning skills to identify patterns, trends, and insights that can help improve customer satisfaction and retention. Provide actionable recommendations based on your analysis. You have access to other agents that can answer questions and provide data about customers, products, orders, inventory, and fulfilment. Use these agents to gather information as needed.",
- "description": "A reasoning agent that can analyze customer data and provide recommendations for improving customer satisfaction and retention.",
+ "system_message": "You are a reasoning agent that can analyze customer and order data and provide recommendations for improving customer satisfaction and retention. You do not have access to any data sources, but you can reason based on the information provided to you by other agents. Use your reasoning skills to identify patterns, trends, and insights that can help improve customer satisfaction and retention. Provide actionable recommendations based on your analysis. You have access to other agents that can answer questions and provide data about customers, products, orders, inventory, and fulfilment. Use these agents to gather information as needed.",
+ "description": "A reasoning agent that can analyze customer and order data and provide recommendations for improving customer satisfaction and retention.",
"use_rag": false,
"use_mcp": false,
"use_bing": false,
diff --git a/data/agent_teams/rfp_analysis_team.json b/data/agent_teams/rfp_analysis_team.json
new file mode 100644
index 000000000..1da46f059
--- /dev/null
+++ b/data/agent_teams/rfp_analysis_team.json
@@ -0,0 +1,72 @@
+{
+ "id": "1",
+ "team_id": "team-clm-1",
+ "name": "RFP Team",
+ "status": "visible",
+ "created": "",
+ "created_by": "",
+ "deployment_name": "gpt-4.1-mini",
+ "description": "A specialized multi-agent team that analyzes RFP and contract documents to summarize content, identify potential risks, check compliance gaps, and provide action plans for contract improvement.",
+ "logo": "",
+ "plan": "",
+ "agents": [
+ {
+ "input_key": "",
+ "type": "summary",
+ "name": "RfpSummaryAgent",
+ "deployment_name": "gpt-4.1-mini",
+ "icon": "",
+ "system_message":"You are the Summary Agent. Your role is to read and synthesize RFP or proposal documents into clear, structured executive summaries. Focus on key clauses, deliverables, evaluation criteria, pricing terms, timelines, and obligations. Organize your output into sections such as Overview, Key Clauses, Deliverables, Terms, and Notable Conditions. Highlight unique or high-impact items that other agents (Risk or Compliance) should review. Be concise, factual, and neutral in tone.",
+ "description": "Summarizes RFP and contract documents into structured, easy-to-understand overviews.",
+ "use_rag": true,
+ "use_mcp": false,
+ "use_bing": false,
+ "use_reasoning": false,
+ "index_name": "macae-rfp-summary-index",
+ "index_foundry_name": "",
+ "index_endpoint": "",
+ "coding_tools": false
+ },
+ {
+ "input_key": "",
+ "type": "risk",
+ "name": "RfpRiskAgent",
+ "deployment_name": "gpt-4.1-mini",
+ "icon": "",
+ "system_message": "You are the Risk Agent. Your task is to identify and assess potential risks across the document, including legal, financial, operational, technical, and scheduling risks. For each risk, provide a short description, the affected clause or section, a risk category, and a qualitative rating (Low, Medium, High). Focus on material issues that could impact delivery, compliance, or business exposure. Summarize findings clearly to support decision-making and escalation.",
+ "description": "Analyzes the dataset for risks such as delivery, financial, operational, and compliance-related vulnerabilities.",
+ "use_rag": true,
+ "use_mcp": false,
+ "use_bing": false,
+ "use_reasoning": false,
+ "index_name": "macae-rfp-risk-index",
+ "coding_tools": false
+ },
+ {
+ "input_key": "",
+ "type": "compliance",
+ "name": "RfpComplianceAgent",
+ "deployment_name": "gpt-4.1-mini",
+ "icon": "",
+ "system_message": "You are the Compliance Agent. Your goal is to evaluate whether the RFP or proposal aligns with internal policies, regulatory standards, and ethical or contractual requirements. Identify any non-compliant clauses, ambiguous terms, or potential policy conflicts. For each issue, specify the related policy area (e.g., data privacy, labor, financial controls) and classify it as Mandatory or Recommended for review. Maintain a professional, objective tone and emphasize actionable compliance insights.",
+ "description": "Checks for compliance gaps against regulations, policies, and standard contracting practices.",
+ "use_rag": true,
+ "use_mcp": false,
+ "use_bing": false,
+ "use_reasoning": false,
+ "index_name": "macae-rfp-compliance-index",
+ "coding_tools": false
+ }
+ ],
+ "protected": false,
+ "starting_tasks": [
+ {
+ "id": "task-1",
+ "name": "RFP Document Summary",
+ "prompt": "I would like to review the Woodgrove Bank RFP response from Contoso",
+ "created": "",
+ "creator": "",
+ "logo": ""
+ }
+ ]
+}
diff --git a/data/datasets/contract_compliance/compliance/Compliance_file.docx b/data/datasets/contract_compliance/compliance/Compliance_file.docx
new file mode 100644
index 000000000..b9afe4f16
Binary files /dev/null and b/data/datasets/contract_compliance/compliance/Compliance_file.docx differ
diff --git a/data/datasets/contract_compliance/compliance/NDA_file.docx b/data/datasets/contract_compliance/compliance/NDA_file.docx
new file mode 100644
index 000000000..95e7a6ce7
Binary files /dev/null and b/data/datasets/contract_compliance/compliance/NDA_file.docx differ
diff --git a/data/datasets/contract_compliance/risk/NDA_file.docx b/data/datasets/contract_compliance/risk/NDA_file.docx
new file mode 100644
index 000000000..95e7a6ce7
Binary files /dev/null and b/data/datasets/contract_compliance/risk/NDA_file.docx differ
diff --git a/data/datasets/contract_compliance/risk/Risks_file.docx b/data/datasets/contract_compliance/risk/Risks_file.docx
new file mode 100644
index 000000000..fd3dcb571
Binary files /dev/null and b/data/datasets/contract_compliance/risk/Risks_file.docx differ
diff --git a/data/datasets/contract_compliance/summary/NDA_file.docx b/data/datasets/contract_compliance/summary/NDA_file.docx
new file mode 100644
index 000000000..95e7a6ce7
Binary files /dev/null and b/data/datasets/contract_compliance/summary/NDA_file.docx differ
diff --git a/data/datasets/retail/customer/customer_churn_analysis.csv b/data/datasets/retail/customer/customer_churn_analysis.csv
new file mode 100644
index 000000000..eaa4c9c24
--- /dev/null
+++ b/data/datasets/retail/customer/customer_churn_analysis.csv
@@ -0,0 +1,6 @@
+ReasonForCancellation,Percentage
+Service Dissatisfaction,40
+Financial Reasons,3
+Competitor Offer,15
+Moving to a Non-Service Area,5
+Other,37
diff --git a/data/datasets/retail/customer/customer_feedback_surveys.csv b/data/datasets/retail/customer/customer_feedback_surveys.csv
new file mode 100644
index 000000000..126f0ca64
--- /dev/null
+++ b/data/datasets/retail/customer/customer_feedback_surveys.csv
@@ -0,0 +1,3 @@
+SurveyID,Date,SatisfactionRating,Comments
+O5678,2023-03-16,5,"Loved the summer dress! Fast delivery."
+O5970,2023-09-13,4,"Happy with the sportswear. Quick delivery."
diff --git a/data/datasets/retail/customer/customer_profile.csv b/data/datasets/retail/customer/customer_profile.csv
new file mode 100644
index 000000000..88bc93b9d
--- /dev/null
+++ b/data/datasets/retail/customer/customer_profile.csv
@@ -0,0 +1,2 @@
+CustomerID,Name,Age,MembershipDuration,TotalSpend,AvgMonthlySpend,PreferredCategories
+C1024,Emily Thompson,35,24,4800,200,"Dresses, Shoes, Accessories"
diff --git a/data/datasets/retail/customer/customer_service_interactions.json b/data/datasets/retail/customer/customer_service_interactions.json
new file mode 100644
index 000000000..f8345bff2
--- /dev/null
+++ b/data/datasets/retail/customer/customer_service_interactions.json
@@ -0,0 +1,3 @@
+{"InteractionID":"1","Channel":"Live Chat","Date":"2023-06-20","Customer":"Emily Thompson","OrderID":"O5789","Content":["Agent: Hello Emily, how can I assist you today?","Emily: Hi, I just received my order O5789, and wanted to swap it for another colour","Agent: Sure, that's fine- feel free to send it back or change it in store.","Emily: Ok, I'll just send it back then","Agent: Certainly. I've initiated the return process. You'll receive an email with the return instructions.","Emily: Thank you."]}
+{"InteractionID":"2","Channel":"Phone Call","Date":"2023-07-25","Customer":"Emily Thompson","OrderID":"O5890","Content":["Agent: Good afternoon, this is Contoso customer service. How may I help you?","Emily: I'm calling about my order O5890. I need the gown for an event this weekend, and just want to make sure it will be delivered on time as it's really important.","Agent: Let me check... it seems like the delivery is on track. It should be there on time.","Emily: Ok thanks."]}
+{"InteractionID":"3","Channel":"Email","Date":"2023-09-15","Customer":"Emily Thompson","OrderID":"","Content":["Subject: Membership Cancellation Request","Body: Hello, I want to cancel my Contoso Plus subscription. The cost is becoming too high for me."]}
diff --git a/data/datasets/retail/customer/email_marketing_engagement.csv b/data/datasets/retail/customer/email_marketing_engagement.csv
new file mode 100644
index 000000000..5d89be28c
--- /dev/null
+++ b/data/datasets/retail/customer/email_marketing_engagement.csv
@@ -0,0 +1,6 @@
+Campaign,Opened,Clicked,Unsubscribed
+Summer Sale,Yes,Yes,No
+New Arrivals,Yes,No,No
+Exclusive Member Offers,No,No,No
+Personal Styling Invite,No,No,No
+Autumn Collection Preview,Yes,Yes,No
diff --git a/data/datasets/retail/customer/loyalty_program_overview.csv b/data/datasets/retail/customer/loyalty_program_overview.csv
new file mode 100644
index 000000000..334261e34
--- /dev/null
+++ b/data/datasets/retail/customer/loyalty_program_overview.csv
@@ -0,0 +1,2 @@
+TotalPointsEarned,PointsRedeemed,CurrentPointBalance,PointsExpiringNextMonth
+4800,3600,1200,1200
diff --git a/data/datasets/retail/customer/social_media_sentiment_analysis.csv b/data/datasets/retail/customer/social_media_sentiment_analysis.csv
new file mode 100644
index 000000000..78ed2ec2d
--- /dev/null
+++ b/data/datasets/retail/customer/social_media_sentiment_analysis.csv
@@ -0,0 +1,8 @@
+Month,PositiveMentions,NegativeMentions,NeutralMentions
+March,500,50,200
+April,480,60,220
+May,450,80,250
+June,400,120,300
+July,350,150,320
+August,480,70,230
+September,510,40,210
diff --git a/data/datasets/retail/customer/store_visit_history.csv b/data/datasets/retail/customer/store_visit_history.csv
new file mode 100644
index 000000000..de5b300a7
--- /dev/null
+++ b/data/datasets/retail/customer/store_visit_history.csv
@@ -0,0 +1,4 @@
+Date,StoreLocation,Purpose,Outcome
+2023-05-12,Downtown Outlet,Browsing,"Purchased a Silk Scarf (O5789)"
+2023-07-20,Uptown Mall,Personal Styling,"Booked a session but didn't attend"
+2023-08-05,Midtown Boutique,Browsing,"No purchase"
diff --git a/data/datasets/retail/customer/subscription_benefits_utilization.csv b/data/datasets/retail/customer/subscription_benefits_utilization.csv
new file mode 100644
index 000000000..c8f07966b
--- /dev/null
+++ b/data/datasets/retail/customer/subscription_benefits_utilization.csv
@@ -0,0 +1,5 @@
+Benefit,UsageFrequency
+Free Shipping,7
+Early Access to Collections,2
+Exclusive Discounts,1
+Personalized Styling Sessions,0
diff --git a/data/datasets/retail/customer/unauthorized_access_attempts.csv b/data/datasets/retail/customer/unauthorized_access_attempts.csv
new file mode 100644
index 000000000..2b66bc4b2
--- /dev/null
+++ b/data/datasets/retail/customer/unauthorized_access_attempts.csv
@@ -0,0 +1,4 @@
+Date,IPAddress,Location,SuccessfulLogin
+2023-06-20,192.168.1.1,Home Network,Yes
+2023-07-22,203.0.113.45,Unknown,No
+2023-08-15,198.51.100.23,Office Network,Yes
diff --git a/data/datasets/retail/customer/website_activity_log.csv b/data/datasets/retail/customer/website_activity_log.csv
new file mode 100644
index 000000000..0f7f6c557
--- /dev/null
+++ b/data/datasets/retail/customer/website_activity_log.csv
@@ -0,0 +1,6 @@
+Date,PagesVisited,TimeSpent
+2023-09-10,"Homepage, New Arrivals, Dresses",15
+2023-09-11,"Account Settings, Subscription Details",5
+2023-09-12,"FAQ, Return Policy",3
+2023-09-13,"Careers Page, Company Mission",2
+2023-09-14,"Sale Items, Accessories",10
diff --git a/data/datasets/retail/order/competitor_pricing_analysis.csv b/data/datasets/retail/order/competitor_pricing_analysis.csv
new file mode 100644
index 000000000..79c8aeedc
--- /dev/null
+++ b/data/datasets/retail/order/competitor_pricing_analysis.csv
@@ -0,0 +1,5 @@
+ProductCategory,ContosoAveragePrice,CompetitorAveragePrice
+Dresses,120,100
+Shoes,100,105
+Accessories,60,55
+Sportswear,80,85
diff --git a/data/datasets/retail/order/delivery_performance_metrics.csv b/data/datasets/retail/order/delivery_performance_metrics.csv
new file mode 100644
index 000000000..9678102bb
--- /dev/null
+++ b/data/datasets/retail/order/delivery_performance_metrics.csv
@@ -0,0 +1,8 @@
+Month,AverageDeliveryTime,OnTimeDeliveryRate,CustomerComplaints
+March,3,98,15
+April,4,95,20
+May,5,92,30
+June,6,88,50
+July,7,85,70
+August,4,94,25
+September,3,97,10
diff --git a/data/datasets/retail/order/product_return_rates.csv b/data/datasets/retail/order/product_return_rates.csv
new file mode 100644
index 000000000..6c5c4c3f3
--- /dev/null
+++ b/data/datasets/retail/order/product_return_rates.csv
@@ -0,0 +1,6 @@
+Category,ReturnRate
+Dresses,15
+Shoes,10
+Accessories,8
+Outerwear,12
+Sportswear,9
diff --git a/data/datasets/retail/order/product_table.csv b/data/datasets/retail/order/product_table.csv
new file mode 100644
index 000000000..79037292c
--- /dev/null
+++ b/data/datasets/retail/order/product_table.csv
@@ -0,0 +1,6 @@
+ProductCategory,ReturnRate,ContosoAveragePrice,CompetitorAveragePrice
+Dresses,15,120,100
+Shoes,10,100,105
+Accessories,8,60,55
+Outerwear,12,,
+Sportswear,9,80,85
diff --git a/data/datasets/retail/order/purchase_history.csv b/data/datasets/retail/order/purchase_history.csv
new file mode 100644
index 000000000..bf4cbdcca
--- /dev/null
+++ b/data/datasets/retail/order/purchase_history.csv
@@ -0,0 +1,8 @@
+OrderID,Name,Date,ItemsPurchased,TotalAmount,DiscountApplied,DateDelivered,ReturnFlag
+O5678,Emily Thompson,2023-03-15,"Summer Floral Dress, Sun Hat",150,10,2023-03-19,No
+O5721,Emily Thompson,2023-04-10,"Leather Ankle Boots",120,15,2023-04-13,No
+O5789,Emily Thompson,2023-05-05,Silk Scarf,80,0,2023-05-25,Yes
+O5832,Emily Thompson,2023-06-18,Casual Sneakers,90,5,2023-06-21,No
+O5890,Emily Thompson,2023-07-22,"Evening Gown, Clutch Bag",300,20,2023-08-05,No
+O5935,Emily Thompson,2023-08-30,Denim Jacket,110,0,2023-09-03,Yes
+O5970,Emily Thompson,2023-09-12,"Fitness Leggings, Sports Bra",130,25,2023-09-18,No
diff --git a/data/datasets/retail/order/warehouse_incident_reports.csv b/data/datasets/retail/order/warehouse_incident_reports.csv
new file mode 100644
index 000000000..e7440fcb2
--- /dev/null
+++ b/data/datasets/retail/order/warehouse_incident_reports.csv
@@ -0,0 +1,4 @@
+Date,IncidentDescription,AffectedOrders
+2023-06-15,Inventory system outage,100
+2023-07-18,Logistics partner strike,250
+2023-08-25,Warehouse flooding due to heavy rain,150
diff --git a/data/datasets/rfp/compliance/rfp_compliance_guidelines.pdf b/data/datasets/rfp/compliance/rfp_compliance_guidelines.pdf
new file mode 100644
index 000000000..53e89678c
Binary files /dev/null and b/data/datasets/rfp/compliance/rfp_compliance_guidelines.pdf differ
diff --git a/data/datasets/rfp/compliance/woodgrove_bank_rfp_response_contoso_ltd.pdf b/data/datasets/rfp/compliance/woodgrove_bank_rfp_response_contoso_ltd.pdf
new file mode 100644
index 000000000..46ab9b4ba
Binary files /dev/null and b/data/datasets/rfp/compliance/woodgrove_bank_rfp_response_contoso_ltd.pdf differ
diff --git a/data/datasets/rfp/risk/rfp_security_risk_guidelines.pdf b/data/datasets/rfp/risk/rfp_security_risk_guidelines.pdf
new file mode 100644
index 000000000..e1534a45f
Binary files /dev/null and b/data/datasets/rfp/risk/rfp_security_risk_guidelines.pdf differ
diff --git a/data/datasets/rfp/risk/woodgrove_bank_rfp_response_contoso_ltd.pdf b/data/datasets/rfp/risk/woodgrove_bank_rfp_response_contoso_ltd.pdf
new file mode 100644
index 000000000..46ab9b4ba
Binary files /dev/null and b/data/datasets/rfp/risk/woodgrove_bank_rfp_response_contoso_ltd.pdf differ
diff --git a/data/datasets/rfp/summary/woodgrove_bank_rfp_response_contoso_ltd.pdf b/data/datasets/rfp/summary/woodgrove_bank_rfp_response_contoso_ltd.pdf
new file mode 100644
index 000000000..46ab9b4ba
Binary files /dev/null and b/data/datasets/rfp/summary/woodgrove_bank_rfp_response_contoso_ltd.pdf differ
diff --git a/docs/ACRBuildAndPushGuide.md b/docs/ACRBuildAndPushGuide.md
index 278451b91..b836ebc7a 100644
--- a/docs/ACRBuildAndPushGuide.md
+++ b/docs/ACRBuildAndPushGuide.md
@@ -74,6 +74,29 @@ If you want to update image tag and image manually you can follow below steps:

+**MCP Server :**
+
+```bash
+az acr login --name
+docker build --no-cache -f src/mcp_server/Dockerfile -t /: ./src/mcp_server
+docker push /:
+```
+
+If you want to update image tag and image manually you can follow below steps:
+- Go to your **Container App** in the [Azure Portal](https://portal.azure.com/#home).
+- In the left menu, select **Containers**.
+- Under your container, update:
+
+ - Image source โ Azure Container Registry / Docker Hub.
+
+ - Image name โ myapp/mcp.
+
+ - Tag โ change to the new one you pushed (e.g., v2).
+
+- Click **Save** โ this will create a new revision automatically with the updated image.
+
+
+
## โ
Verification
Run the following command to verify that images were pushed successfully:
diff --git a/docs/AVMPostDeploymentGuide.md b/docs/AVMPostDeploymentGuide.md
index 5041ea51b..6233cecda 100644
--- a/docs/AVMPostDeploymentGuide.md
+++ b/docs/AVMPostDeploymentGuide.md
@@ -9,6 +9,7 @@ This document provides guidance on post-deployment steps after deploying the Mul
## Overview
After deploying the infrastructure using AVM, you'll need to complete the application layer setup, which includes:
+
- Configuring team agent configurations
- Processing and uploading sample datasets
- Setting up Azure AI Search indexes
@@ -25,14 +26,15 @@ Before starting the post-deployment process, ensure you have the following:
2. **[Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)** (v2.50+) - Command-line tool for managing Azure resources
-3. **[Python](https://www.python.org/downloads/)** (v3.9+ recommended) - Required for data processing scripts
+3. **[Python](https://www.python.org/downloads/)** (v4.9+ recommended) - Required for data processing scripts
4. **[Git](https://git-scm.com/downloads/)** - Version control system for cloning the repository
### Azure Requirements
5. **Azure Access** - One of the following roles on the subscription or resource group:
- - `Contributor`
+
+ - `Contributor`
- `Owner`
6. **Deployed Infrastructure** - A successful Multi-Agent Custom Automation Engine deployment from the [AVM repository](https://github.com/Azure/bicep-registry-modules/tree/main/avm/ptn/sa/multi-agent-custom-automation-engine)
@@ -56,6 +58,7 @@ First, clone this repository to access the post-deployment scripts:
```powershell
git clone https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator.git
```
+
```powershell
cd Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
```
@@ -65,6 +68,7 @@ cd Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
The post-deployment process is automated through a single PowerShell or Bash script that completes the following tasks in approximately 5-10 minutes:
#### What the Script Does:
+
1. **Configure Team Agent Settings** - Upload HR, Marketing, and Retail team configurations
2. **Process Sample Datasets** - Upload and index sample customer data, analytics, and business metrics
3. **Set Up Azure AI Search** - Create and configure search indexes for agent data retrieval
@@ -72,35 +76,20 @@ The post-deployment process is automated through a single PowerShell or Bash scr
#### Execute the Script:
-1. **Choose the appropriate command based on your deployment method and OS:**
-
- **If you deployed using custom templates, ARM/Bicep deployments, or `az deployment group` commands:**
+1. **Run the appropriate command based on your OS:**
- **For PowerShell (Windows/Linux/macOS):**
- ```powershell
- .\infra\scripts\Team-Config-And-Data.ps1 -ResourceGroup ""
- ```
- - **For Bash (Linux/macOS/WSL):**
- ```bash
- bash infra/scripts/team_config_and_data.sh ""
- ```
-
- **If you deployed using `azd up` command:**
-
- - **For PowerShell (Windows/Linux/macOS):**
```powershell
- .\infra\scripts\Team-Config-And-Data.ps1
+ infra\scripts\Selecting-Team-Config-And-Data.ps1 -ResourceGroup ""
```
- **For Bash (Linux/macOS/WSL):**
```bash
- bash infra/scripts/team_config_and_data.sh
+ bash infra/scripts/selecting_team_config_and_data.sh --resource-group ""
```
-
- > **Note**: Replace `` with the actual name of the resource group containing your deployed Azure resources.
- > **๐ก Tip**: Since this guide is for AVM deployments, you'll most likely use the first command with the `-ResourceGroup` parameter.
+ > **Note**: Replace `` with the actual name of the resource group containing your deployed Azure resources.
### Step 3: Provide Required Information
@@ -110,6 +99,7 @@ During script execution, you'll be prompted for:
- Select the appropriate Azure subscription
#### Resource Validation
+
- The script will automatically detect and validate your deployed Azure resources
- Confirmation prompts will appear before making configuration changes
@@ -121,4 +111,4 @@ Upon successful completion, you'll see a success message.
### Step 5: Set Up App Authentication (Optional)
-Follow the steps in [Set Up Authentication in Azure App Service](azure_app_service_auth_setup.md) to add app authentication to your web app running on Azure App Service.
\ No newline at end of file
+Follow the steps in [Set Up Authentication in Azure App Service](azure_app_service_auth_setup.md) to add app authentication to your web app running on Azure App Service.
diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md
index 1fc24810c..c88dedad3 100644
--- a/docs/CustomizingAzdParameters.md
+++ b/docs/CustomizingAzdParameters.md
@@ -29,6 +29,8 @@ By default this template will use the environment name as the prefix to prevent
| `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md) | Set this if you want to reuse an existing Log Analytics Workspace instead of creating a new one. |
| `AZURE_ENV_VM_ADMIN_USERNAME` | string | `take(newGuid(), 20)` | The administrator username for the virtual machine. |
| `AZURE_ENV_VM_ADMIN_PASSWORD` | string | `newGuid()` | The administrator password for the virtual machine. |
+| `AZURE_ENV_VM_SIZE` | string | `Standard_D2s_v5` | The size of the virtual machine deployed with private networking. |
+| `AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT` | string | `` | Sets container registry used by backend, frontend and Mcp containers. |
---
## How to Set a Parameter
diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md
index 49e31605a..1f931d165 100644
--- a/docs/DeploymentGuide.md
+++ b/docs/DeploymentGuide.md
@@ -1,203 +1,251 @@
# Deployment Guide
-## **Pre-requisites**
+## Overview
-To deploy this solution accelerator, ensure you have access to an [Azure subscription](https://azure.microsoft.com/free/) with the necessary permissions to create **resource groups, resources, app registrations, and assign roles at the resource group level**. This should include Contributor role at the subscription level and Role Based Access Control role on the subscription and/or resource group level. Follow the steps in [Azure Account Set Up](../docs/AzureAccountSetUp.md).
+This guide walks you through deploying the Multi Agent Custom Automation Engine Solution Accelerator to Azure. The deployment process takes approximately 9-10 minutes for the default Development/Testing configuration and includes both infrastructure provisioning and application setup.
-> **Note:** When you deploy this solution, you will automatically be granted access to interact with the Cosmos DB database that stores your application data. Specifically, you'll have permissions to:
-> - Read database information and settings
-> - Create, modify, and delete data storage containers (think of these as folders for organizing your data)
-> - Add, view, update, and remove individual data records within those containers
+๐ **Need Help?** If you encounter any issues during deployment, check our [Troubleshooting Guide](./TroubleShootingSteps.md) for solutions to common problems.
-Check the [Azure Products by Region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=all®ions=all) page and select a **region** where the following services are available:
+> **Note**: Some tenants may have additional security restrictions that run periodically and could impact the application (e.g., blocking public network access). If you experience issues or the application stops working, check if these restrictions are the cause. In such cases, consider deploying the WAF-supported version to ensure compliance. To configure, [Click here](#31-choose-deployment-type-optional).
+## Step 1: Prerequisites & Setup
+
+### 1.1 Azure Account Requirements
+
+Ensure you have access to an [Azure subscription](https://azure.microsoft.com/free/) with the following permissions:
+
+| **Required Permission/Role** | **Scope** | **Purpose** |
+|------------------------------|-----------|-------------|
+| **Contributor** | Subscription level | Create and manage Azure resources |
+| **User Access Administrator** | Subscription level | Manage user access and role assignments |
+| **Role Based Access Control Admin** | Subscription/Resource Group level | Configure RBAC permissions |
+| **App Registration Creation** | Azure Active Directory | Create and configure authentication |
+
+**๐ How to Check Your Permissions:**
+
+1. Go to [Azure Portal](https://portal.azure.com/)
+2. Navigate to **Subscriptions** (search for "subscriptions" in the top search bar)
+3. Click on your target subscription
+4. In the left menu, click **Access control (IAM)**
+5. Scroll down to see the table with your assigned roles - you should see:
+ - **Contributor**
+ - **User Access Administrator**
+ - **Role Based Access Control Administrator** (or similar RBAC role)
+
+**For App Registration permissions:**
+1. Go to **Microsoft Entra ID** โ **Manage** โ **App registrations**
+2. Try clicking **New registration**
+3. If you can access this page, you have the required permissions
+4. Cancel without creating an app registration
+
+๐ **Detailed Setup:** Follow [Azure Account Set Up](./AzureAccountSetUp.md) for complete configuration.
+
+### 1.2 Check Service Availability & Quota
+
+โ ๏ธ **CRITICAL:** Before proceeding, ensure your chosen region has all required services available:
+
+**Required Azure Services:**
- [Azure AI Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/)
+- [Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-services/openai/)
+- [Azure AI Search](https://learn.microsoft.com/en-us/azure/search/)
+- [Azure App Service](https://learn.microsoft.com/en-us/azure/app-service/)
- [Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/)
- [Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/)
- [Azure Cosmos DB](https://learn.microsoft.com/en-us/azure/cosmos-db/)
- [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/)
-- [Azure AI Search](https://learn.microsoft.com/en-us/azure/search/)
+- [Azure Blob Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/)
+- [Azure Queue Storage](https://learn.microsoft.com/en-us/azure/storage/queues/)
- [GPT Model Capacity](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models)
-Here are some example regions where the services are available: East US, East US2, Japan East, UK South, Sweden Central.
+**Recommended Regions:** East US, East US2, Australia East, Japan East, UK South, France Central
-### **Important Note for PowerShell Users**
+๐ **Check Availability:** Use [Azure Products by Region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/) to verify service availability.
-If you encounter issues running PowerShell scripts due to the policy of not being digitally signed, you can temporarily adjust the `ExecutionPolicy` by running the following command in an elevated PowerShell session:
+### 1.3 Quota Check (Optional)
-```powershell
-Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
-```
+๐ก **RECOMMENDED:** Check your Azure OpenAI quota availability before deployment for optimal planning.
-This will allow the scripts to run for the current session without permanently changing your system's policy.
+๐ **Follow:** [Quota Check Instructions](./quota_check.md) to ensure sufficient capacity.
-### **Azure Developer CLI (azd) Requirement**
+**Default Quota Configuration:**
+- **GPT-4.1:** 150k tokens
+- **o4-mini:** 50k tokens
+- **GPT-4.1-mini:** 50k tokens
-Ensure that you are using the latest version of the [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview).
-The `azd` version must be **1.18.0 or higher**.
+> **Note:** When you run `azd up`, the deployment will automatically show you regions with available quota, so this pre-check is optional but helpful for planning purposes. You can customize these settings later in [Step 3.3: Advanced Configuration](#33-advanced-configuration-optional).
-Upgrade commands by OS:
+๐ **Adjust Quota:** Follow [Azure AI Model Quota Settings](./AzureGPTQuotaSettings.md) if needed.
-* **Windows (using winget):**
+## Step 2: Choose Your Deployment Environment
- ```bash
- winget install microsoft.azd
- ```
+Select one of the following options to deploy the Multi Agent Custom Automation Engine Solution Accelerator:
-* **Linux (using apt):**
+### Environment Comparison
- ```bash
- curl -fsSL https://aka.ms/install-azd.sh | bash
- ```
+| **Option** | **Best For** | **Prerequisites** | **Setup Time** |
+|------------|--------------|-------------------|----------------|
+| **GitHub Codespaces** | Quick deployment, no local setup required | GitHub account | ~3-5 minutes |
+| **VS Code Dev Containers** | Fast deployment with local tools | Docker Desktop, VS Code | ~5-10 minutes |
+| **VS Code Web** | Quick deployment, no local setup required | Azure account | ~2-4 minutes |
+| **Local Environment** | Enterprise environments, full control | All tools individually | ~15-30 minutes |
-* **macOS (using Homebrew):**
+**๐ก Recommendation:** For fastest deployment, start with **GitHub Codespaces** - no local installation required.
- ```bash
- brew update && brew tap azure/azd && brew install azd
- ```
+---
-## Deployment Options & Steps
+
+Option A: GitHub Codespaces (Easiest)
-### Deployment Steps
+[](https://codespaces.new/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator)
-Pick from the options below to see step-by-step instructions for GitHub Codespaces, VS Code Dev Containers, Local Environments, and Bicep deployments.
+1. Click the badge above (may take several minutes to load)
+2. Accept default values on the Codespaces creation page
+3. Wait for the environment to initialize (includes all deployment tools)
+4. Proceed to [Step 3: Configure Deployment Settings](#step-3-configure-deployment-settings)
-| [](https://codespaces.new/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) | [](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) |
-| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+
- Deploy in GitHub Codespaces
-
-### GitHub Codespaces
+Option B: VS Code Dev Containers
-You can run this solution using GitHub Codespaces. The button will open a web-based VS Code instance in your browser:
+[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator)
-1. Open the solution accelerator (this may take several minutes):
+**Prerequisites:**
+- [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed and running
+- [VS Code](https://code.visualstudio.com/) with [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
- [](https://codespaces.new/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator)
-
-2. Accept the default values on the create Codespaces page.
-3. Open a terminal window if it is not already open.
-4. Continue with the [deployment options](#deployment-options).
+**Steps:**
+1. Start Docker Desktop
+2. Click the badge above to open in Dev Containers
+3. Wait for the container to build and start (includes all deployment tools)
+4. Proceed to [Step 3: Configure Deployment Settings](#step-3-configure-deployment-settings)
- Deploy in VS Code Dev Containers
+Option C: Visual Studio Code Web
-### VS Code Dev Containers
+### VS Code Web
-You can run this solution in VS Code Dev Containers, which will open the project in your local VS Code using the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers):
+[&message=Open&color=blue&logo=visualstudiocode&logoColor=white)](https://vscode.dev/azure/?vscode-azure-exp=foundry&agentPayload=eyJiYXNlVXJsIjogImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9taWNyb3NvZnQvTXVsdGktQWdlbnQtQ3VzdG9tLUF1dG9tYXRpb24tRW5naW5lLVNvbHV0aW9uLUFjY2VsZXJhdG9yL3JlZnMvaGVhZHMvbWFpbi9pbmZyYS92c2NvZGVfd2ViIiwgImluZGV4VXJsIjogIi9pbmRleC5qc29uIiwgInZhcmlhYmxlcyI6IHsiYWdlbnRJZCI6ICIiLCAiY29ubmVjdGlvblN0cmluZyI6ICIiLCAidGhyZWFkSWQiOiAiIiwgInVzZXJNZXNzYWdlIjogIiIsICJwbGF5Z3JvdW5kTmFtZSI6ICIiLCAibG9jYXRpb24iOiAiIiwgInN1YnNjcmlwdGlvbklkIjogIiIsICJyZXNvdXJjZUlkIjogIiIsICJwcm9qZWN0UmVzb3VyY2VJZCI6ICIiLCAiZW5kcG9pbnQiOiAiIn0sICJjb2RlUm91dGUiOiBbImFpLXByb2plY3RzLXNkayIsICJweXRob24iLCAiZGVmYXVsdC1henVyZS1hdXRoIiwgImVuZHBvaW50Il19)
-1. Start Docker Desktop (install it if not already installed).
-2. Open the project:
+1. Click the badge above (may take a few minutes to load)
+2. Sign in with your Azure account when prompted
+3. Select the subscription where you want to deploy the solution
+4. Wait for the environment to initialize (includes all deployment tools)
+5. When prompted in the VS Code Web terminal, choose one of the available options shown below:
- [](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator)
+ 
+
+6. **Authenticate with Azure** (VS Code Web requires device code authentication):
+ ```shell
+ az login --use-device-code
+ ```
+ > **Note:** In VS Code Web environment, the regular `az login` command may fail. Use the `--use-device-code` flag to authenticate via device code flow. Follow the prompts in the terminal to complete authentication.
-3. In the VS Code window that opens, once the project files show up (this may take several minutes), open a terminal window.
-4. Continue with the [deployment options](#deployment-options).
+7. Proceed to [Step 3: Configure Deployment Settings](#step-3-configure-deployment-settings)
- Deploy in your local Environment
+Option D: Local Environment
-### Local Environment
+**Required Tools:**
+- [PowerShell 7.0+](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell)
+- [Azure Developer CLI (azd) 1.18.0+](https://aka.ms/install-azd)
+- [Python 3.9+](https://www.python.org/downloads/)
+- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
+- [Git](https://git-scm.com/downloads)
-If you're not using one of the above options for opening the project, then you'll need to:
-
-1. Make sure the following tools are installed:
-
- - [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.5) (v7.0+) - available for Windows, macOS, and Linux.
- - [Azure Developer CLI (azd)](https://aka.ms/install-azd) (v1.18.0+) - version
- - [Python 3.9+](https://www.python.org/downloads/)
- - [Docker Desktop](https://www.docker.com/products/docker-desktop/)
- - [Git](https://git-scm.com/downloads)
-
-2. Clone the repository or download the project code via command-line:
+**Setup Steps:**
+1. Install all required deployment tools listed above
+2. Clone the repository:
```shell
azd init -t microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator/
```
-
> **โ ๏ธ Warning:** The `azd init` command will download and initialize the project template. If you run this command in a directory that already contains project files, it may override your existing changes. Only run this command once when setting up the project for the first time. If you need to update an existing project, consider using `git pull` or manually downloading updates instead.
-3. Open the project folder in your terminal or editor.
-4. Continue with the [deployment options](#deployment-options).
+3. Open the project folder in your terminal
+4. Proceed to [Step 3: Configure Deployment Settings](#step-3-configure-deployment-settings)
+
+**PowerShell Users:** If you encounter script execution issues, run:
+```powershell
+Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
+```
-### Deployment Options
+## Step 3: Configure Deployment Settings
-The [`infra`](../infra) folder of the Multi Agent Solution Accelerator contains the [`main.bicep`](../infra/main.bicep) Bicep script, which defines all Azure infrastructure components for this solution.
+Review the configuration options below. You can customize any settings that meet your needs, or leave them as defaults to proceed with a standard deployment.
-By default, the `azd up` command uses the [`main.parameters.json`](../infra/main.parameters.json) file to deploy the solution. This file is pre-configured for a **sandbox environment** โ ideal for development and proof-of-concept scenarios, with minimal security and cost controls for rapid iteration.
+### 3.1 Choose Deployment Type (Optional)
-For **production deployments**, the repository also provides [`main.waf.parameters.json`](../infra/main.waf.parameters.json), which applies a [Well-Architected Framework (WAF) aligned](https://learn.microsoft.com/en-us/azure/well-architected/) configuration. This option enables additional Azure best practices for reliability, security, cost optimization, operational excellence, and performance efficiency, such as:
+| **Aspect** | **Development/Testing (Default)** | **Production** |
+|------------|-----------------------------------|----------------|
+| **Configuration File** | `main.parameters.json` (sandbox) | Copy `main.waf.parameters.json` to `main.parameters.json` |
+| **Security Controls** | Minimal (for rapid iteration) | Enhanced (production best practices) |
+| **Cost** | Lower costs | Cost optimized |
+| **Use Case** | POCs, development, testing | Production workloads |
+| **Framework** | Basic configuration | [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/well-architected/) |
+| **Features** | Core functionality | Reliability, security, operational excellence |
- **Prerequisite** โ Enable the Microsoft.Compute/EncryptionAtHost feature for every subscription (and region, if required) where you plan to deploy VMs or VM scale sets with `encryptionAtHost: true`. Repeat the registration steps below for each target subscription (and for each region when applicable). This step is required for **WAF-aligned** (production) deployments.
+**To use production configuration:**
- Steps to enable the feature:
- 1. Set the target subscription:
- Run: az account set --subscription "<YourSubscriptionId>"
- 2. Register the feature (one time per subscription):
- Run: az feature register --name EncryptionAtHost --namespace Microsoft.Compute
- 3. Wait until registration completes and shows "Registered":
- Run: az feature show --name EncryptionAtHost --namespace Microsoft.Compute --query properties.state -o tsv
- 4. Refresh the provider (if required):
- Run: az provider register --namespace Microsoft.Compute
- 5. Re-run the deployment after registration is complete.
+**Prerequisite** โ Enable the Microsoft.Compute/EncryptionAtHost feature for every subscription (and region, if required) where you plan to deploy VMs or VM scale sets with `encryptionAtHost: true`. Repeat the registration steps below for each target subscription (and for each region when applicable). This step is required for **WAF-aligned** (production) deployments.
- Note: Feature registration can take several minutes. Ensure the feature is registered before attempting deployments that require encryptionAtHost.
+Steps to enable the feature:
- Reference: Azure Host Encryption โ https://learn.microsoft.com/azure/virtual-machines/disks-enable-host-based-encryption-portal?tabs=azure-cli
+1. Set the target subscription:
+ Run: az account set --subscription "<YourSubscriptionId>"
+2. Register the feature (one time per subscription):
+ Run: az feature register --name EncryptionAtHost --namespace Microsoft.Compute
+3. Wait until registration completes and shows "Registered":
+ Run: az feature show --name EncryptionAtHost --namespace Microsoft.Compute --query properties.state -o tsv
+4. Refresh the provider (if required):
+ Run: az provider register --namespace Microsoft.Compute
+5. Re-run the deployment after registration is complete.
- - Enhanced network security (e.g., Network protection with private endpoints)
- - Stricter access controls and managed identities
- - Logging, monitoring, and diagnostics enabled by default
- - Resource tagging and cost management recommendations
+Note: Feature registration can take several minutes. Ensure the feature is registered before attempting deployments that require encryptionAtHost.
-**How to choose your deployment configuration:**
+Reference: Azure Host Encryption โ https://learn.microsoft.com/azure/virtual-machines/disks-enable-host-based-encryption-portal?tabs=azure-cli
-* Use the default `main.parameters.json` file for a **sandbox/dev environment**
-* For a **WAF-aligned, production-ready deployment**, copy the contents of `main.waf.parameters.json` into `main.parameters.json` before running `azd up`
+Copy the contents from the production configuration file to your main parameters file:
----
+1. Navigate to the `infra` folder in your project
+2. Open `main.waf.parameters.json` in a text editor (like Notepad, VS Code, etc.)
+3. Select all content (Ctrl+A) and copy it (Ctrl+C)
+4. Open `main.parameters.json` in the same text editor
+5. Select all existing content (Ctrl+A) and paste the copied content (Ctrl+V)
+6. Save the file (Ctrl+S)
-### VM Credentials Configuration
+### 3.2 Set VM Credentials (Optional - Production Deployment Only)
-By default, the solution sets the VM administrator username and password from environment variables.
-If you do not configure these values, a randomly generated GUID will be used for both the username and password.
+> **Note:** This section only applies if you selected **Production** deployment type in section 3.1. VMs are not deployed in the default Development/Testing configuration.
-To set your own VM credentials before deployment, use:
+By default, random GUIDs are generated for VM credentials. To set custom credentials:
-```sh
+```shell
azd env set AZURE_ENV_VM_ADMIN_USERNAME
azd env set AZURE_ENV_VM_ADMIN_PASSWORD
```
-> [!TIP]
-> Always review and adjust parameter values (such as region, capacity, security settings and log analytics workspace configuration) to match your organizationโs requirements before deploying. For production, ensure you have sufficient quota and follow the principle of least privilege for all identities and role assignments.
-
-
-> [!IMPORTANT]
-> The WAF-aligned configuration is under active development. More Azure Well-Architected recommendations will be added in future updates.
-
-
-Consider the following settings during your deployment to modify specific settings:
+### 3.3 Advanced Configuration (Optional)
- Configurable Deployment Settings
+Configurable Parameters
-When you start the deployment, most parameters will have **default values**, but you can update the following settings [here](../docs/CustomizingAzdParameters.md):
+You can customize various deployment settings before running `azd up`, including Azure regions, AI model configurations (deployment type, version, capacity), container registry settings, and resource names.
+๐ **Complete Guide:** See [Parameter Customization Guide](./CustomizingAzdParameters.md) for the full list of available parameters and their usage.
[Optional] Quota Recommendations
-By default, the **GPT model capacity** in deployment is set to **140k tokens**.
+By default, the **GPT model capacity** in deployment is set to **150k tokens**.
To adjust quota settings, follow these [steps](./AzureGPTQuotaSettings.md).
@@ -206,382 +254,286 @@ To adjust quota settings, follow these [steps](./AzureGPTQuotaSettings.md).
+Reuse Existing Resources
- Reusing an Existing Log Analytics Workspace
+To optimize costs and integrate with your existing Azure infrastructure, you can configure the solution to reuse compatible resources already deployed in your subscription.
- Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md)
+**Supported Resources for Reuse:**
-
+- **Log Analytics Workspace:** Integrate with your existing monitoring infrastructure by reusing an established Log Analytics workspace for centralized logging and monitoring. [Configuration Guide](./re-use-log-analytics.md)
-
+- **Azure AI Foundry Project:** Leverage your existing AI Foundry project and deployed models to avoid duplication and reduce provisioning time. [Configuration Guide](./re-use-foundry-project.md)
- Reusing an Existing Azure AI Foundry Project
+**Key Benefits:**
+- **Cost Optimization:** Eliminate duplicate resource charges
+- **Operational Consistency:** Maintain unified monitoring and AI infrastructure
+- **Faster Deployment:** Skip resource creation for existing compatible services
+- **Simplified Management:** Reduce the number of resources to manage and monitor
- Guide to get your [Existing Project ID](/docs/re-use-foundry-project.md)
+**Important Considerations:**
+- Ensure existing resources meet the solution's requirements and are in compatible regions
+- Review access permissions and configurations before reusing resources
+- Consider the impact on existing workloads when sharing resources
-### Deploying with AZD
+## Step 4: Deploy the Solution
-Once you've opened the project in [Codespaces](#github-codespaces), [Dev Containers](#vs-code-dev-containers), or [locally](#local-environment), you can deploy it to Azure by following these steps:
+๐ก **Before You Start:** If you encounter any issues during deployment, check our [Troubleshooting Guide](./TroubleShootingSteps.md) for common solutions.
-#### Important: Environment Management for Redeployments
+โ ๏ธ **Critical: Redeployment Warning** - If you have previously run `azd up` in this folder (i.e., a `.azure` folder exists), you must [create a fresh environment](#creating-a-new-environment) to avoid conflicts and deployment failures.
-> **โ ๏ธ Critical:** If you're redeploying or have deployed this solution before, you **must** create a fresh environment to avoid conflicts and deployment failures.
+### 4.1 Authenticate with Azure
-**Choose one of the following before deployment:**
-
-**Option A: Create a completely new environment (Recommended)**
```shell
-azd env new
+azd auth login
```
-**Option B: Reinitialize in a new directory**
+> **Note for VS Code Web Users:** If you're using VS Code Web and have already authenticated using `az login --use-device-code` in Option C, you may skip this step or proceed with `azd auth login` if prompted.
+
+**For specific tenants:**
```shell
-# Navigate to a new directory
-cd ../my-new-deployment
-azd init -t microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator/
+azd auth login --tenant-id
```
-> **๐ก Why is this needed?** Azure resources maintain state information tied to your environment. Reusing an old environment can cause naming conflicts, permission issues, and deployment failures.
+**Finding Tenant ID:**
+1. Open the [Azure Portal](https://portal.azure.com/)
+2. Navigate to **Microsoft Entra ID** from the left-hand menu
+3. Under the **Overview** section, locate the **Tenant ID** field. Copy the value displayed
-#### Deploy the solution
-1. Login to Azure:
+### 4.2 Start Deployment
+**NOTE:** If you are running the latest azd version (version 1.23.9), please run the following command.
+```bash
+azd config set provision.preflight off
+```
- ```shell
- azd auth login
- ```
+```shell
+azd up
+```
- #### To authenticate with Azure Developer CLI (`azd`), use the following command with your **Tenant ID**:
+**During deployment, you'll be prompted for:**
+1. **Environment name** (e.g., "macaedev") - Must be 3-16 characters long, alphanumeric only
+2. **Azure subscription** selection
+3. **Azure AI Foundry deployment region** - Select a region with available model quota for AI operations
+4. **Primary location** - Select the region where your infrastructure resources will be deployed
+5. **Resource group** selection (create new or use existing)
- ```sh
- azd auth login --tenant-id
- ```
+**Expected Duration:** 9-10 minutes for default configuration
-2. Provision and deploy all the resources:
+- **Upon successful completion**, you will see a success message indicating that all resources have been deployed, along with the application URL and next steps for uploading team configurations and sample data.
- ```shell
- azd up
- ```
- > **Note:** This solution accelerator requires **Azure Developer CLI (azd) version 1.18.0 or higher**. Please ensure you have the latest version installed before proceeding with deployment. [Download azd here](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd).
+
-3. Provide an `azd` environment name (e.g., "macaeapp").
-4. Select a subscription from your Azure account and choose a location that has quota for all the resources.
+**โ ๏ธ Deployment Issues:** If you encounter errors or timeouts, try a different region as there may be capacity constraints. For detailed error solutions, see our [Troubleshooting Guide](./TroubleShootingSteps.md).
- - This deployment will take _4-6 minutes_ to provision the resources in your account and set up the solution with sample data.
- - If you encounter an error or timeout during deployment, changing the location may help, as there could be availability constraints for the resources.
- - **Upon successful completion**, you will see a success message indicating that all resources have been deployed, along with the application URL and next steps for uploading team configurations and sample data.
+### 4.3 Get Application URL
-5. Upload Team Configurations using command printed in the terminal. The command will look like one of the following. Run the appropriate command for your shell from the project root:
+After successful deployment:
+1. Open [Azure Portal](https://portal.azure.com/)
+2. Navigate to your resource group
+3. Find the Frontend App Service
+4. Copy the **Default domain**
- - **For Bash (Linux/macOS/WSL):**
- ```bash
- bash infra/scripts/upload_team_config.sh
- ```
+โ ๏ธ **Important:** Complete [Post-Deployment Steps](#step-5-post-deployment-configuration) before accessing the application.
- - **For PowerShell (Windows):**
- ```powershell
- infra\scripts\Upload-Team-Config.ps1
- ```
+## Step 5: Post-Deployment Configuration
-6. Index Sample Data into Search Service using command printed in the terminal. The command will look like one of the following. Run the appropriate command for your shell from the project root:
+### 5.1 Run Post Deployment Script
- - **For Bash (Linux/macOS/WSL):**
- ```bash
- bash infra/scripts/process_sample_data.sh
- ```
-
- - **For PowerShell (Windows):**
- ```powershell
- infra\scripts\Process-Sample-Data.ps1
- ```
-
-7. **[Alternative]** To upload team configurations and index sample data in one step, run the appropriate command for your shell from the project root:
+1. You can upload Team Configurations using command printed in the terminal. The command will look like one of the following. Run the appropriate command for your shell from the project root:
- **For Bash (Linux/macOS/WSL):**
```bash
- bash infra/scripts/team_config_and_data.sh
+ bash infra/scripts/selecting_team_config_and_data.sh
```
- **For PowerShell (Windows):**
```powershell
- infra\scripts\Team-Config-And-Data.ps1
+ infra\scripts\Selecting-Team-Config-And-Data.ps1
```
-8. **[Optional]** Set up authentication for your web application by following the steps in [Set Up Authentication in Azure App Service](../docs/azure_app_service_auth_setup.md).
-
-9. **Access your application:** Open the [Azure Portal](https://portal.azure.com/), go to your resource group, find the App Service that runs the frontend application, and get the application URL from the **Default domain** field.
-### ๐ ๏ธ Troubleshooting
- If you encounter any issues during the deployment process, please refer [troubleshooting](../docs/TroubleShootingSteps.md) document for detailed steps and solutions.
+2. After executing the above script, the system will present available use case scenarios for selection. You can choose individual scenarios or deploy all use cases simultaneously. Upon selection, the corresponding datasets and configuration files for the chosen use case(s) will be uploaded to your Azure environment.
- ## Next Steps
-Now that you've completed your deployment, you can start using the solution.
+
-To help you get started, here are some [Sample Questions](./SampleQuestions.md) you can follow to try it out.
-## Clean Up Resources
-If you are done trying out the application, you can delete all resources by running:
+### 5.2 Configure Authentication (Optional)
-```shell
-azd down
-```
-> **Note:** If you deployed with `enableRedundancy=true` and Log Analytics workspace replication is enabled, you must first disable replication before running `azd down` else resource group delete will fail. Follow the steps in [Handling Log Analytics Workspace Deletion with Replication Enabled](./LogAnalyticsReplicationDisable.md), wait until replication returns `false`, then run `azd down`.
+1. Follow [App Authentication Configuration](./azure_app_service_auth_setup.md)
+2. Wait up to 10 minutes for authentication changes to take effect
-# Local setup
+### 5.3 Verify Deployment
-> **Note for macOS Developers**: If you are using macOS on Apple Silicon (ARM64) the DevContainer will **not** work. This is due to a limitation with the Azure Functions Core Tools (see [here](https://github.com/Azure/azure-functions-core-tools/issues/3112)).
+1. Access your application using the URL from Step 4.3
+2. Confirm the application loads successfully
+
-The easiest way to run this accelerator is in a VS Code Dev Containers, which will open the project in your local VS Code using the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers):
+### 5.4 Test the Application
-1. Start Docker Desktop (install it if not already installed)
-1. Open the project:
- [](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator)
+**Quick Test Steps:**
-1. In the VS Code window that opens, once the project files show up (this may take several minutes), open a terminal window
+1. **Access the application** using the URL from Step 4.3
+2. **Sign in** with your authenticated account
+3. **Select a use case** from the available scenarios you uploaded in Step 5.1
+4. **Ask a sample question** relevant to the selected use case
+5. **Verify the response** includes appropriate multi-agent collaboration
+6. **Check the logs** in Azure Portal to confirm backend processing
-## Detailed Development Container setup instructions
+๐ **Detailed Instructions:** See the complete [Sample Workflow](./SampleQuestions.md) guide for step-by-step testing procedures and sample questions for each use case.
-The solution contains a [development container](https://code.visualstudio.com/docs/remote/containers) with all the required tooling to develop and deploy the accelerator. To deploy the Multi-Agent solutions accelerator using the provided development container you will also need:
-- [Visual Studio Code](https://code.visualstudio.com)
-- [Remote containers extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
+## Step 6: Clean Up (Optional)
-If you are running this on Windows, we recommend you clone this repository in [WSL](https://code.visualstudio.com/docs/remote/wsl)
+### Remove All Resources
-```cmd
-git clone https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
-```
-
-Open the cloned repository in Visual Studio Code and connect to the development container.
+To purge resources and clean up after deployment, use the `azd down` command or follow the Delete Resource Group Guide for manual cleanup through Azure Portal.
-```cmd
-code .
+```shell
+azd down
```
+> **Note:** If you deployed with `enableRedundancy=true` and Log Analytics workspace replication is enabled, you must first disable replication before running `azd down` else resource group delete will fail. Follow the steps in [Handling Log Analytics Workspace Deletion with Replication Enabled](./LogAnalyticsReplicationDisable.md), wait until replication returns `false`, then run `azd down`.
-!!! tip
-Visual Studio Code should recognize the available development container and ask you to open the folder using it. For additional details on connecting to remote containers, please see the [Open an existing folder in a container](https://code.visualstudio.com/docs/remote/containers#_quick-start-open-an-existing-folder-in-a-container) quickstart.
-
-When you start the development container for the first time, the container will be built. This usually takes a few minutes. **Please use the development container for all further steps.**
-
-The files for the dev container are located in `/.devcontainer/` folder.
-
-## Local deployment and debugging:
-
-1. **Clone the repository.**
-
-2. **Log into the Azure CLI:**
-
- - Check your login status using:
- ```bash
- az account show
- ```
- - If not logged in, use:
- ```bash
- az login
- ```
- - To specify a tenant, use:
- ```bash
- az login --tenant
- ```
-
-3. **Create a Resource Group:**
-
- - You can create it either through the Azure Portal or the Azure CLI:
- ```bash
- az group create --name --location EastUS2
- ```
-
-4. **Deploy the Bicep template:**
-
- - You can use the Bicep extension for VSCode (Right-click the `.bicep` file, then select "Show deployment plan") or use the Azure CLI:
- ```bash
- az deployment group create -g -f infra/main.bicep --query 'properties.outputs'
- ```
- - **Note**: You will be prompted for a `principalId`, which is the ObjectID of your user in Entra ID. To find it, use the Azure Portal or run:
+### Manual Cleanup (if needed)
+If deployment fails or you need to clean up manually:
+- Follow [Delete Resource Group Guide](./DeleteResourceGroup.md)
- ```bash
- az ad signed-in-user show --query id -o tsv
- ```
+## Managing Multiple Environments
- You will also be prompted for locations for Cosmos and OpenAI services. This is to allow separate regions where there may be service quota restrictions.
+### Recover from Failed Deployment
- - **Additional Notes**:
+If your deployment failed or encountered errors, here are the steps to recover:
- **Role Assignments in Bicep Deployment:**
+
+Recover from Failed Deployment
- The **main.bicep** deployment includes the assignment of the appropriate roles to AOAI and Cosmos services. If you want to modify an existing implementationโfor example, to use resources deployed as part of the simple deployment for local debuggingโyou will need to add your own credentials to access the Cosmos and AOAI services. You can add these permissions using the following commands:
+**If your deployment failed or encountered errors:**
- ```bash
- az cosmosdb sql role assignment create --resource-group --account-name --role-definition-name "Cosmos DB Built-in Data Contributor" --principal-id --scope /subscriptions//resourceGroups//providers/Microsoft.DocumentDB/databaseAccounts/
- ```
+1. **Try a different region:** Create a new environment and select a different Azure region during deployment
+2. **Clean up and retry:** Use `azd down` to remove failed resources, then `azd up` to redeploy
+3. **Check troubleshooting:** Review [Troubleshooting Guide](./TroubleShootingSteps.md) for specific error solutions
+4. **Fresh start:** Create a completely new environment with a different name
- ```bash
- az role assignment create --assignee --role "Azure AI User" --scope /subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts/
- ```
+**Example Recovery Workflow:**
+```shell
+# Remove failed deployment (optional)
+azd down
- **Using a Different Database in Cosmos:**
+# Create new environment (3-16 chars, alphanumeric only)
+azd env new macaeretry
- You can set the solution up to use a different database in Cosmos. For example, you can name it something like macae-dev. To do this:
+# Deploy with different settings/region
+azd up
+```
- 1. Change the environment variable **COSMOSDB_DATABASE** to the new database name.
- 2. You will need to create the database in the Cosmos DB account. You can do this from the Data Explorer pane in the portal, click on the drop down labeled "_+ New Container_" and provide all the necessary details.
+
-5. **Create a `.env` file:**
+### Creating a New Environment
- - Navigate to the `src\backend` folder and create a `.env` file based on the provided `.env.sample` file.
- - Update the `.env` file with the required values from your Azure resource group in Azure Portal App Service environment variables.
- - Alternatively, if resources were
- provisioned using `azd provision` or `azd up`, a `.env` file is automatically generated in the `.azure//.env`
- file. You can copy the contents of this file into your backend `.env` file.
+If you need to deploy to a different region, test different configurations, or create additional environments:
- _**Note**: To get your `` run `azd env list` to see which env is default._
+
+Create a New Environment
-6. **Fill in the `.env` file:**
+**Create Environment Explicitly:**
+```shell
+# Create a new named environment (3-16 characters, alphanumeric only)
+azd env new
- - Use the output from the deployment or check the Azure Portal under "Deployments" in the resource group.
- - Make sure to set APP_ENV to "**dev**" in `.env` file.
- - For local development, make sure to include below env variables in the `.env`
- - `BACKEND_API_URL=http://localhost:8000`
- - `FRONTEND_SITE_NAME=http://127.0.0.1:3000`
- - `MCP_SERVER_ENDPOINT=http://localhost:9000/mcp`.
+# Select the new environment
+azd env select
-7. **(Optional) Set up a virtual environment:**
+# Deploy to the new environment
+azd up
+```
- - If you are using `venv`, create and activate your virtual environment for both the frontend and backend folders.
+**Example:**
+```shell
+# Create a new environment for production (valid: 3-16 chars)
+azd env new macaeprod
-8. **Install requirements - Backend:**
+# Switch to the new environment
+azd env select macaeprod
- - To install the requirement for backend -
- Open a terminal in the `src/backend` folder and run:
- ```bash
- pip install uv
- uv sync
- ```
-
-9. **Build the frontend (important):**
+# Deploy with fresh settings
+azd up
+```
- - To install the requirement for frontend -
- Open a terminal in the `src/frontend` folder and run:
- ```bash
- pip install -r requirements.txt
- ```
+> **Environment Name Requirements:**
+> - **Length:** 3-16 characters
+> - **Characters:** Alphanumeric only (letters and numbers)
+> - **Valid examples:** `macae`, `test123`, `myappdev`, `prod2025`
+> - **Invalid examples:** `co` (too short), `my-very-long-environment-name` (too long), `test_env` (underscore not allowed)
- - Before running the frontend server, you must build the frontend to generate the necessary `build/assets` directory.
+
- From the `src/frontend` directory, run:
+
+Switch Between Environments
- ```bash
- npm install
- npm run build
- ```
+**List Available Environments:**
+```shell
+azd env list
+```
-10. **Install requirements - MCP server:**
+**Switch to Different Environment:**
+```shell
+azd env select
+```
- - To install the requirement for mcp server -
- Open a terminal in the `src/mcp_server` folder and run:
- ```bash
- pip install uv
- uv sync
- ```
+**View Current Environment:**
+```shell
+azd env get-values
+```
-11. **Run the application:**
+
-- From the `src/backend` directory activate the virtual environment created through step 8 and Run:
+### Best Practices for Multiple Environments
-```bash
-python app_kernel.py
-```
+- **Use descriptive names:** `macaedev`, `macaeprod`, `macaetest` (remember: 3-16 chars, alphanumeric only)
+- **Different regions:** Deploy to multiple regions for testing quota availability
+- **Separate configurations:** Each environment can have different parameter settings
+- **Clean up unused environments:** Use `azd down` to remove environments you no longer need
-- In a new terminal from the src/frontend directory
+## Next Steps
-```bash
- python frontend_server.py
-```
+Now that your deployment is complete and tested, explore these resources to enhance your experience:
-or Run
+๐ **Learn More:**
+- [Local Development Setup](./LocalDevelopmentSetup.md) - Set up your local development environment
+- [Sample Questions](./SampleQuestions.md) - Explore sample questions and workflows
+- [MCP Server Documentation](./mcp_server.md) - Learn about Model Context Protocol server integration
+- [Customizing Parameters](./CustomizingAzdParameters.md) - Advanced configuration options
+- [Azure Account Setup](./AzureAccountSetUp.md) - Detailed Azure subscription configuration
- ```bash
- npm run dev
- ```
+## Need Help?
-- From the `src/mcp_server` directory activate the virtual environment created through step 10 and Run:
+- ๐ **Issues:** Check [Troubleshooting Guide](./TroubleShootingSteps.md)
+- ๐ฌ **Support:** Review [Support Guidelines](../SUPPORT.md)
+- ๐ง **Development:** See [Contributing Guide](../CONTRIBUTING.md)
-```bash
-python mcp_server.py --transport streamable-http --host 0.0.0.0 --port 9000
-```
+## Advanced: Deploy Local Changes
-11. Open a browser and navigate to `http://localhost:3000`
-12. To see swagger API documentation, you can navigate to `http://localhost:8000/docs`
+If you've made local modifications to the code and want to deploy them to Azure, follow these steps to swap the configuration files:
-## Deploy Your local changes
-To Deploy your local changes rename the below files.
- 1. Rename `azure.yaml` to `azure_custom2.yaml` and `azure_custom.yaml` to `azure.yaml`.
- 2. Go to `infra` directory
- - Rename `main.bicep` to `main_custom2.bicep` and `main_custom.bicep` to `main.bicep`.
-Continue with the [deploying steps](#deploying-with-azd).
+> **Note:** To set up and run the application locally for development, see the [Local Development Setup Guide](./LocalDevelopmentSetup.md).
+### Step 1: Rename Azure Configuration Files
-## Debugging the solution locally
+**In the root directory:**
+1. Rename `azure.yaml` to `azure_custom2.yaml`
+2. Rename `azure_custom.yaml` to `azure.yaml`
-You can debug the API backend running locally with VSCode using the following launch.json entry:
+### Step 2: Rename Infrastructure Files
-```
- {
- "name": "Debug Backend (FastAPI)",
- "type": "debugpy",
- "request": "launch",
- "program": "${workspaceFolder}/src/backend/app_kernel.py",
- "cwd": "${workspaceFolder}/src/backend",
- "console": "integratedTerminal",
- "justMyCode": false,
- "python": "${workspaceFolder}/src/backend/.venv/Scripts/python.exe",
- "env": {
- "PYTHONPATH": "${workspaceFolder}/src/backend",
- "UVICORN_LOG_LEVEL": "debug"
- },
- "args": [],
- "serverReadyAction": {
- "pattern": "Uvicorn running on (https?://[^\\s]+)",
- "uriFormat": "%s",
- "action": "openExternally"
- }
- }
-```
+**In the `infra` directory:**
+1. Rename `main.bicep` to `main_custom2.bicep`
+2. Rename `main_custom.bicep` to `main.bicep`
-To debug the python server in the frontend directory (frontend_server.py) and related, add the following launch.json entry:
+### Step 3: Deploy Changes
-```
- {
- "name": "Python Debugger: Frontend",
- "type": "debugpy",
- "request": "launch",
- "cwd": "${workspaceFolder}/src/frontend",
- "module": "uvicorn",
- "args": ["frontend_server:app", "--port", "3000", "--reload"],
- "jinja": true
- }
+Run the deployment command:
+```shell
+azd up
```
-To debug the MCP server by adding the following launch.json entry:
-
-```
- {
- "name": "Debug MCP Server",
- "type": "debugpy",
- "request": "launch",
- "program": "${workspaceFolder}/src/mcp_server/mcp_server.py",
- "cwd": "${workspaceFolder}/src/mcp_server",
- "console": "integratedTerminal",
- "justMyCode": false,
- "python": "${workspaceFolder}/src/mcp_server/.venv/Scripts/python.exe",
- "env": {
- "PYTHONPATH": "${workspaceFolder}/src/mcp_server"
- },
- "args": [
- "--transport", "streamable-http",
- "--host", "0.0.0.0",
- "--port", "9000"
- ]
- }
-```
+> **Note:** These custom files are configured to deploy your local code changes instead of pulling from the GitHub repository.
diff --git a/docs/LocalDevelopmentSetup.md b/docs/LocalDevelopmentSetup.md
new file mode 100644
index 000000000..ab074f384
--- /dev/null
+++ b/docs/LocalDevelopmentSetup.md
@@ -0,0 +1,608 @@
+# Local Development Setup Guide
+
+This guide provides comprehensive instructions for setting up the Multi Agent Custom Automation Engine Solution Accelerator for local development across Windows and Linux platforms.
+
+## Important Setup Notes
+
+### Multi-Service Architecture
+
+This application consists of **three separate services** that run independently:
+
+1. **Backend** - Backend APIs related to orchestration and Teams
+2. **Frontend** - React-based user interface
+3. **MCP Server** - MCP server to tools call
+
+> **โ ๏ธ Critical: Each service must run in its own terminal/console window**
+>
+> - **Do NOT close terminals** while services are running
+> - Open **3 separate terminal windows** for local development
+> - Each service will occupy its terminal and show live logs
+>
+> **Terminal Organization:**
+> - **Terminal 1**: Backend - HTTP server on port 8000
+> - **Terminal 2**: Frontend - Development server on port 3000
+> - **Terminal 3**: MCP server - HTTP streamable server on port 9000
+
+### Path Conventions
+
+**All paths in this guide are relative to the repository root directory:**
+
+```bash
+Multi-Agent-Custom-Automation-Engine-Solution-Accelerator/ โ Repository root (start here)
+โโโ src/
+โ โโโ backend/ โ cd src/backend
+โ โ โโโ .venv/ โ Virtual environment
+โ โ โโโ .env โ Backend config file
+โ โ โโโ app.py โ Backend Entry Point
+โ โโโ App/ โ cd src/App
+โ โ โโโ node_modules/ โ npm dependencies
+โ โ โโโ .venv/ โ Virtual environment
+โ โ โโโ frontend_server.py โ Frontend entry point
+โ โโโ mcp_server/ โ cd src/mcp_server
+โ โ โโโ .venv/ โ Virtual environment
+โ โ โโโ mcp_server.py โ MCP server Entry Point
+โโโ docs/ โ Documentation (you are here)
+```
+
+**Before starting any step, ensure you are in the repository root directory:**
+
+```bash
+# Verify you're in the correct location
+pwd # Linux/macOS - should show: .../Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
+Get-Location # Windows PowerShell - should show: ...\Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
+
+# If not, navigate to repository root
+cd path/to/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
+```
+
+### Configuration Files
+
+This project uses Backend `.env` file in Backend directory with different configuration requirements:
+
+- **Backend**: `src/backend/.env`
+
+
+
+## Step 1: Prerequisites - Install Required Tools
+
+### Windows Development
+
+#### Option 1: Native Windows (PowerShell)
+
+```powershell
+# Install Python 3.12+ and Git
+winget install Python.Python.3.12
+winget install Git.Git
+
+# Install Node.js for frontend
+winget install OpenJS.NodeJS.LTS
+
+# Install uv package manager
+py -3.12 -m pip install uv
+```
+
+#### Option 2: Windows with WSL2 (Recommended)
+
+```bash
+# Install WSL2 first (run in PowerShell as Administrator):
+# wsl --install -d Ubuntu
+
+# Then in WSL2 Ubuntu terminal:
+sudo apt update && sudo apt install python3.12 python3.12-venv git curl nodejs npm -y
+
+# Install uv
+curl -LsSf https://astral.sh/uv/install.sh | sh
+source ~/.bashrc
+```
+
+### Linux Development
+
+#### Ubuntu/Debian
+
+```bash
+# Install prerequisites
+sudo apt update && sudo apt install python3.12 python3.12-venv git curl nodejs npm -y
+
+# Install uv package manager
+curl -LsSf https://astral.sh/uv/install.sh | sh
+source ~/.bashrc
+```
+
+#### RHEL/CentOS/Fedora
+
+```bash
+# Install prerequisites
+sudo dnf install python3.12 python3.12-devel git curl gcc nodejs npm -y
+
+# Install uv
+curl -LsSf https://astral.sh/uv/install.sh | sh
+source ~/.bashrc
+```
+
+### Clone the Repository
+
+```bash
+git clone https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator.git
+cd Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
+```
+
+## Step 2: Development Tools Setup
+
+### Visual Studio Code (Recommended)
+
+#### Required Extensions
+
+Create `.vscode/extensions.json` in the workspace root and copy the following JSON:
+
+```json
+{
+ "recommendations": [
+ "ms-python.python",
+ "ms-python.pylint",
+ "ms-python.black-formatter",
+ "ms-python.isort",
+ "ms-vscode-remote.remote-wsl",
+ "ms-vscode-remote.remote-containers",
+ "redhat.vscode-yaml",
+ "ms-vscode.azure-account",
+ "ms-python.mypy-type-checker"
+ ]
+}
+```
+
+VS Code will prompt you to install these recommended extensions when you open the workspace.
+
+#### Settings Configuration
+
+Create `.vscode/settings.json` and copy the following JSON:
+
+```json
+{
+ "python.defaultInterpreterPath": "${workspaceFolder}/src/backend/.venv/Scripts/python.exe",
+ "python.terminal.activateEnvironment": true,
+ "python.linting.enabled": true,
+ "python.linting.pylintEnabled": false,
+ "python.linting.flake8Enabled": true,
+ "python.formatting.provider": "black",
+ "python.debugging.logLevel": "Debug",
+ "debug.terminal.clearBeforeReusing": true,
+ "debug.onTaskErrors": "showErrors",
+ "debug.showBreakpointsInOverviewRuler": true,
+ "debug.inlineValues": "on",
+ "files.exclude": {
+ "**/__pycache__": true,
+ "**/*.pyc": true
+ },
+ "python.analysis.extraPaths": [
+ "${workspaceFolder}/src/backend",
+ "${workspaceFolder}/src/mcp_server"
+ ],
+ "folders": [
+ {
+ "name": "Backend",
+ "path": "./src/backend"
+ },
+ {
+ "name": "MCP Server",
+ "path": "./src/mcp_server"
+ },
+ {
+ "name": "Frontend",
+ "path": "./src/App"
+ }
+ ]
+}
+```
+
+## Step 3: Azure Authentication Setup
+
+Before configuring services, authenticate with Azure:
+
+```bash
+# Login to Azure CLI
+az login
+
+# Set your subscription
+az account set --subscription "your-subscription-id"
+
+# Verify authentication
+az account show
+```
+
+
+### Required Azure RBAC Permissions
+
+To run the application locally, your Azure account needs the following role assignments on the deployed resources:
+
+The **main.bicep** deployment includes the assignment of the appropriate roles to AOAI, Storage account, Search service and Cosmos services. If you want to use resource group which is not deployed by you for local debuggingโyou will need to add your own credentials to access the Cosmos Storage account, Search service and AOAI services. You can add these permissions using the following commands:
+
+#### Get Your Microsoft Entra ID User Object ID (Principal ID)
+
+Your Azure AD User Object ID (also called Principal ID) is required for the role assignments below. Choose either method to obtain it:
+
+**Method 1: Using Azure CLI (Recommended)**
+```bash
+az ad signed-in-user show --query id -o tsv
+```
+
+**Method 2: Using Azure Portal**
+1. Go to [Azure Portal](https://portal.azure.com)
+2. Open **Microsoft Entra ID** (or search "Entra")
+3. In the left menu, select **Users**
+4. Select your account
+5. Under **Identity**, copy the **Object ID**
+
+> **Note:** The `` and `` in the commands below refer to the same ID obtained from either method above.
+
+#### Get Your Azure AD User Principal Name (UPN)
+
+Your Azure AD User Principal Name (UPN) is your sign-in email address and is required for some role assignments. Choose either method to obtain it:
+
+**Method 1: Using Azure CLI (Recommended)**
+```bash
+az ad signed-in-user show --query userPrincipalName -o tsv
+```
+
+**Method 2: Using Azure Portal**
+1. Go to [Azure Portal](https://portal.azure.com)
+2. Open **Microsoft Entra ID** (or search "Entra")
+3. In the left menu, select **Users**
+4. Select your account
+5. Copy the **User principal name** (typically your email address, e.g., user@domain.com)
+
+> **Note:** The `` in the commands below refers to your User Principal Name obtained from either method above.
+
+#### Cosmos DB Access
+
+```bash
+# Assign Cosmos DB Built-in Data Contributor role
+az cosmosdb sql role assignment create --resource-group --account-name --role-definition-name "Cosmos DB Built-in Data Contributor" --principal-id --scope /subscriptions//resourceGroups//providers/Microsoft.DocumentDB/databaseAccounts/
+```
+
+#### AI Foundry Access
+
+**To get your AI Foundry Project Resource ID:**
+1. Go to [Azure Portal](https://portal.azure.com)
+2. Navigate to your AI Foundry Project resource
+3. In the **Project details** section, find and copy the **Project resource ID**
+4. The format should be: `/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects/`
+
+> **Note:** For AI Foundry, you need the complete project resource ID path (not just the account name). Use the full path shown in the Project resource ID field.
+
+**Assign the required roles:**
+
+```bash
+# Azure AI User role
+az role assignment create --assignee --role "Azure AI User" --scope /subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects/
+```
+
+```bash
+# Azure AI Developer role
+az role assignment create --assignee --role "Azure AI Developer" --scope /subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects/
+```
+
+```bash
+# Cognitive Services OpenAI User role
+az role assignment create --assignee --role "Cognitive Services OpenAI User" --scope /subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects/
+```
+
+#### Search Service Access
+
+```bash
+az role assignment create --assignee --role "Search Index Data Contributor" --scope /subscriptions//resourceGroups//providers/Microsoft.Search/searchServices/
+```
+
+#### Storage Account Access
+
+```bash
+az role assignment create --assignee --role "Storage Blob Data Contributor" --scope /subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/
+```
+
+
+**Note**: RBAC permission changes can take 5-10 minutes to propagate. If you encounter "Forbidden" errors after assigning roles, wait a few minutes and try again.
+
+## Step 4: Backend Setup & Run Instructions
+
+> **๐ Terminal Reminder**: Open a **dedicated terminal window (Terminal 1)** for the Backend service. All commands in this section assume you start from the **repository root directory**.
+
+### 4.1. Navigate to Backend Directory
+
+```bash
+cd src/backend
+```
+
+### 4.2. Configure Backend Environment Variables
+
+**Step 1: Create the `.env` file**
+
+```bash
+# Create .env file
+touch .env # Linux
+# or
+New-Item .env # Windows PowerShell
+```
+
+**Step 2: Copy the template**
+
+1. Open the `.env.sample` file
+2. Select all content (CTRL + A)
+3. Copy (CTRL + C)
+4. Open the new `.env` file
+5. Paste (CTRL + V)
+
+**Step 3: Get Azure values and update `.env`**
+
+1. Open [Azure Portal](https://portal.azure.com)
+2. Navigate to your **Resource Group**
+3. Open the **Backend Container App**
+4. Click **Environment variables** in the left menu
+5. Copy each value from Azure and update the corresponding variable in your `.env` file
+
+For reference:
+
+
+**Step 4: Update local development settings**
+
+In your `.env` file, make these changes:
+
+- Set `APP_ENV=dev`
+- Keep these local URLs (do NOT change them):
+ - `BACKEND_API_URL=http://localhost:8000`
+ - `FRONTEND_SITE_NAME=*`
+ - `MCP_SERVER_ENDPOINT=http://localhost:9000/mcp`
+
+### 4.3. Install Backend Dependencies
+
+```bash
+# Create and activate virtual environment
+uv venv .venv
+
+# Activate virtual environment
+source .venv/bin/activate # Linux/WSL2
+# or
+.\.venv\Scripts\Activate.ps1 # Windows PowerShell
+
+# Install dependencies
+uv sync --python 3.12
+```
+
+**Windows users**: If you encounter issues with the `uv` command not being found, use the Python Launcher instead:
+
+```powershell
+# Create virtual environment
+py -3.12 -m uv venv .venv
+
+# Install dependencies
+py -3.12 -m uv sync
+```
+
+> **โ ๏ธ Important**: Always run `uv sync` (or `py -3.12 -m uv sync` on Windows) after creating the virtual environment to install all required dependencies. Missing dependencies will cause runtime errors like `ModuleNotFoundError: No module named 'pydantic'` or DNS resolution failures.
+
+### 4.4. Run the Backend
+
+```bash
+python app.py
+```
+
+The Backend API will start at:
+- API: `http://localhost:8000`
+- API Documentation: `http://localhost:8000/docs`
+
+## Step 5: MCP Server Setup & Run Instructions
+
+> **๐ Terminal Reminder**: Open a **second dedicated terminal window (Terminal 2)** for the MCP Server. Keep Terminal 1 (Backend) running. All commands assume you start from the **repository root directory**.
+
+
+### 5.1. Navigate to MCP Server Directory
+
+```bash
+# From repository root
+cd src/mcp_server
+```
+
+
+
+### 5.2. Install MCP Server Dependencies
+
+```bash
+# Create and activate virtual environment
+uv venv .venv
+
+# Activate virtual environment
+source .venv/bin/activate # Linux/WSL2
+# or
+.\.venv\Scripts\Activate.ps1 # Windows PowerShell
+
+# Install dependencies
+uv sync --python 3.12
+```
+
+### 5.3. Run the MCP Server
+
+```bash
+
+# Run with uvicorn
+python mcp_server.py --transport streamable-http --host 0.0.0.0 --port 9000
+```
+
+## Step 6: Frontend (UI) Setup & Run Instructions
+
+> **๐ Terminal Reminder**: Open a **third dedicated terminal window (Terminal 3)** for the Frontend. Keep Terminals 1 (Backend) and 2 (MCP Server) running. All commands assume you start from the **repository root directory**.
+
+The UI is located under `src/App`.
+
+### 6.1. Navigate to Frontend Directory
+
+```bash
+# From repository root
+cd src/App
+```
+
+### 6.2. Install UI Dependencies
+
+```bash
+python -m venv .venv
+
+# Activate virtual environment
+source .venv/bin/activate # Linux/WSL2
+# or
+.\.venv\Scripts\Activate.ps1 # Windows PowerShell
+
+
+pip install -r requirements.txt
+```
+
+```bash
+npm install
+```
+
+### 6.3. Build the UI
+
+```bash
+npm run build
+```
+
+### 6.4. Start Development Server
+
+```bash
+python frontend_server.py
+```
+
+The app will start at:
+
+```
+http://localhost:3000
+```
+
+## Troubleshooting
+
+### Common Issues
+
+#### Python Version Issues
+
+```bash
+# Check available Python versions
+python3 --version
+python3.12 --version
+
+# If python3.12 not found, install it:
+# Ubuntu: sudo apt install python3.12
+# Windows: winget install Python.Python.3.12
+```
+
+#### Virtual Environment Issues
+
+```bash
+# Recreate virtual environment
+rm -rf .venv # Linux
+# or Remove-Item -Recurse .venv # Windows PowerShell
+
+uv venv .venv
+# Activate and reinstall
+source .venv/bin/activate # Linux
+# or .\.venv\Scripts\Activate.ps1 # Windows
+uv sync --python 3.12
+```
+
+#### Permission Issues (Linux)
+
+```bash
+# Fix ownership of files
+sudo chown -R $USER:$USER .
+
+# Fix uv permissions
+chmod +x ~/.local/bin/uv
+```
+
+#### Windows-Specific Issues
+
+```powershell
+# PowerShell execution policy
+Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+# Long path support (Windows 10 1607+, run as Administrator)
+New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
+
+# SSL certificate issues
+pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org uv
+```
+
+### Environment Variable Issues
+
+```bash
+# Check environment variables are loaded
+env | grep AZURE # Linux
+Get-ChildItem Env:AZURE* # Windows PowerShell
+
+# Validate .env file format
+cat .env | grep -v '^#' | grep '=' # Should show key=value pairs
+```
+
+## Step 7: Verify All Services Are Running
+
+Before using the application, confirm all three services are running in separate terminals:
+
+### Terminal Status Checklist
+
+| Terminal | Service | Command | Expected Output | URL |
+|----------|---------|---------|-----------------|-----|
+| **Terminal 1** | Backend | `python app.py` | `INFO: Application startup complete.` | http://localhost:8000 |
+| **Terminal 2** | MCP Server | `python mcp_server.py --transport streamable-http --host 0.0.0.0 --port 9000` | `INFO: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)` | http://localhost:9000 |
+| **Terminal 3** | Frontend | `python frontend_server.py` | `Local: http://localhost:3000/` | http://localhost:3000 |
+
+### Quick Verification
+
+**1. Check Backend :**
+- Look at Terminal 1 output
+- Should see regular messages: `INFO: Application startup complete.`
+- No error messages
+
+**2. Check MCP Server:**
+- Look at Terminal 2 output
+- Should see regular polling messages: `INFO: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)`
+- No error messages
+
+**3. Check Frontend:**
+- Open browser to http://localhost:3000
+- Should see the Application UI
+
+
+### Common Issues
+
+**Service not starting?**
+- Ensure you're in the correct directory
+- Verify virtual environment is activated (Python services)
+- Check that port is not already in use (8000 for API, 3000 for frontend, 9000 for MCP server)
+- Review error messages in the terminal
+
+**Can't access services?**
+- Verify firewall isn't blocking ports 8000 or 3000 or 9000
+- Try `http://localhost:port` instead of `http://127.0.0.1:port`
+- Ensure services show "startup complete" messages
+
+## Step 8: Next Steps
+
+Once all services are running (as confirmed in Step 7), you can:
+
+1. **Access the Application**: Open `http://localhost:3000` in your browser to explore the frontend UI
+2. **Try a Sample Workflow**: Follow [SampleQuestions.md](SampleQuestions.md) for a guided walkthrough of the Multi agent process
+3. **Explore the Codebase**: Start with `src/backend/app.py` to understand the agent architecture
+
+
+## Related Documentation
+
+- [Deployment Guide](DeploymentGuide.md) - Production deployment instructions
diff --git a/docs/ManualAzureDeployment.md b/docs/ManualAzureDeployment.md
index e2dd964d6..19d6ba7e5 100644
--- a/docs/ManualAzureDeployment.md
+++ b/docs/ManualAzureDeployment.md
@@ -54,7 +54,7 @@ az acr login --name
## Build and push the image
-Build the frontend and backend Docker images and push them to your Azure Container Registry. Run the following from the src/backend and the src/frontend directory contexts:
+Build the frontend and backend Docker images and push them to your Azure Container Registry. Run the following from the src/backend and the src/App directory contexts:
```sh
az acr build \
diff --git a/docs/SampleQuestions.md b/docs/SampleQuestions.md
index 89f0ed0c1..1ad2f1a4a 100644
--- a/docs/SampleQuestions.md
+++ b/docs/SampleQuestions.md
@@ -6,10 +6,12 @@ To help you get started, here are some **Sample Prompts** you can ask in the app
## **Teams**
Select the Team option from the top-left section, then click Continue after choosing the desired team.
-By default, three teams are available after running the post-deployment scripts:
+> _Note: Five teams are available only if all teams are selected during post-deployment; otherwise, only selected teams are visible._
- Retail
- HR
- Marketing
+- RFP
+- Contract Compliance

@@ -73,5 +75,47 @@ _Sample operation:_
> _Observe: It goes into "Thinking Process", "Processing your plan" and "coordinating with AI Agents"_
> _Review the output._
+### **RFP Analysis Scenario**
+If you select the RFP team, follow the prompts below.
+
+>**Agents Used:** RFP Summary, RFP Risk, RFP Compliance
+
+The RFP Analysis Scenario allows users to explore and analyze Request for Proposal (RFP) and contract documents. Key tasks include:
+
+_Sample operation:_
+
+- Task: Switch to the **"RFP Team"** from the top left section and click **"Continue"** button.
+- Task: From the Quick Tasks, select **"RFP Document Summary"** and submit it.
+
+ > _Note: Average response time is 10โ15 minutes._
+ > _Observe: It will trigger the "Generating Plan Action" and give the Proposed Plan with 5 or more Steps_
+
+
+- Task: Click on **"Approve Task Plan"** Button.
+ > _Note: Average response time is around 01 minute._
+ > _Observe: It goes into "Thinking Process", "Processing your plan" and "coordinating with AI Agents"._
+ > _Review the output._
+
+### **Contract Compliance Review Scenario**
+If you select the Contract Compliance team, follow the prompts below.
+
+>**Agents Used:** Contract Summary, Contract Risk, Contract Compliance
+
+The Contract Compliance Review Scenario allows users to explore and analyze NDA and contract documents for compliance and risk assessment. Key tasks include:
+
+_Sample operation:_
+
+- Task: Switch to the **"Contract Compliance Review Team"** from the top left section and click **"Continue"** button.
+- Task: From the Quick Tasks, select **"NDA Contract Review"** and submit it.
+
+ > _Note: Average response time is 10โ15 minutes._
+ > _Observe: It will trigger the "Generating Plan Action" and give the Proposed Plan with 4 or more Steps_
+
+
+- Task: Click on **"Approve Task Plan"** Button.
+ > _Note: Average response time is around 01 minute._
+ > _Observe: It goes into "Thinking Process", "Processing your plan" and "coordinating with AI Agents"._
+ > _Review the output._
+
This structured approach ensures that users receive automated, AI-coordinated task execution and intelligent responses from specialized agents.
diff --git a/docs/TroubleShootingSteps.md b/docs/TroubleShootingSteps.md
index 01f39d44e..3b83d504d 100644
--- a/docs/TroubleShootingSteps.md
+++ b/docs/TroubleShootingSteps.md
@@ -4,589 +4,166 @@ When deploying Azure resources, you may come across different error codes that s
Use these as quick reference guides to unblock your deployments.
-## Error Codes
+## โก Most Frequently Encountered Errors
+
+| Error Code | Common Cause | Full Details |
+|------------|--------------|--------------|
+| **InsufficientQuota** | Not enough quota available in subscription | [View Solution](#quota--capacity-limitations) |
+| **MissingSubscriptionRegistration** | Required feature not registered in subscription | [View Solution](#subscription--access-issues) |
+| **ResourceGroupNotFound** | RG doesn't exist or using old .env file | [View Solution](#resource-group--deployment-management) |
+| **DeploymentModelNotSupported** | Model not available in selected region | [View Solution](#regional--location-issues) |
+| **DeploymentNotFound** | Deployment record not found or was deleted | [View Solution](#resource-group--deployment-management) |
+| **ResourceNotFound** | Resource does not exist or cannot be found | [View Solution](#resource-identification--references) |
+| **SpecialFeatureOrQuotaIdRequired** | Subscription lacks access to specific model | [View Solution](#subscription--access-issues) |
+| **ContainerAppOperationError** | Improperly built container image | [View Solution](#miscellaneous) |
+| **ServiceUnavailable** | Service not available in selected region | [View Solution](#regional--location-issues) |
+| **BadRequest - DatabaseAccount is in a failed provisioning state** | Previous deployment failed | [View Solution](#resource-state--provisioning) |
+| **Unauthorized - Operation cannot be completed without additional quota** | Insufficient quota for requested operation | [View Solution](#subscription--access-issues) |
+| **ResourceGroupBeingDeleted** | Resource group deletion in progress | [View Solution](#resource-group--deployment-management) |
+| **FlagMustBeSetForRestore** | Soft-deleted resource requires restore flag or purge | [View Solution](#miscellaneous) |
+| **ParentResourceNotFound** | Parent resource does not exist or cannot be found | [View Solution](#resource-identification--references) |
+| **AccountProvisioningStateInvalid** | Resource used before provisioning completed | [View Solution](#resource-state--provisioning) |
+| **InternalSubscriptionIsOverQuotaForSku** | Subscription quota exceeded for the requested SKU | [View Solution](#quota--capacity-limitations) |
+| **InvalidResourceGroup** | Invalid resource group configuration | [View Solution](#resource-group--deployment-management) |
+| **RequestDisallowedByPolicy** | Azure Policy blocking the requested operation | [View Solution](#subscription--access-issues) |
+
+## ๐ Table of Contents
+
+- [Subscription & Access Issues](#subscription--access-issues)
+- [Quota & Capacity Limitations](#quota--capacity-limitations)
+- [Regional & Location Issues](#regional--location-issues)
+- [Resource Naming & Validation](#resource-naming--validation)
+- [Resource Identification & References](#resource-identification--references)
+- [Network & Infrastructure Configuration](#network--infrastructure-configuration)
+- [Configuration & Property Errors](#configuration--property-errors)
+- [Resource State & Provisioning](#resource-state--provisioning)
+- [Miscellaneous](#miscellaneous)
+
+## Subscription & Access Issues
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------|-------------|------------------|
+| **ReadOnlyDisabledSubscription** | Subscription is disabled or in read-only state | Check if you have an active subscription before starting the deployment Depending on the type of the Azure Subscription, the expiration date might have been reached You have to activate the Azure Subscription before creating any Azure resource Refer to [Reactivate a disabled Azure subscription](https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/subscription-disabled) documentation |
+| **MissingSubscriptionRegistration/ AllowBringYourOwnPublicIpAddress** | Required feature not registered in subscription | **Enable `AllowBringYourOwnPublicIpAddress` Feature** Before deploying the resources, you may need to enable the **Bring Your Own Public IP Address** feature in Azure. This is required only once per subscription. **Steps:** Run the following command to register the feature: `az feature register --namespace Microsoft.Network --name AllowBringYourOwnPublicIpAddress` Wait for the registration to complete. Check the status using: `az feature show --namespace Microsoft.Network --name AllowBringYourOwnPublicIpAddress --query properties.state` The output should show: "Registered" Once the feature is registered, refresh the provider: `az provider register --namespace Microsoft.Network` ๐ก Note: Feature registration may take several minutes to complete. This needs to be done only once per Azure subscription. |
+| **Unauthorized - Operation cannot be completed without additional quota** | Insufficient quota for requested operation | Check your quota usage using: `az vm list-usage --location "" -o table` To request more quota refer to [VM Quota Request](https://techcommunity.microsoft.com/blog/startupsatmicrosoftblog/how-to-increase-quota-for-specific-types-of-azure-virtual-machines/3792394) |
+| **CrossTenantDeploymentNotPermitted** | Deployment across different Azure AD tenants not allowed | **Check tenant match:** Ensure your deployment identity (user/SP) and the target resource group are in the same tenant: `az account show` `az group show --name ` **Verify pipeline/service principal:** If using CI/CD, confirm the service principal belongs to the same tenant and has permissions on the resource group **Avoid cross-tenant references:** Make sure your Bicep doesn't reference subscriptions, resource groups, or resources in another tenant **Test minimal deployment:** Deploy a simple resource to the same resource group to confirm identity and tenant are correct **Guest/external accounts:** Avoid using guest users from other tenants; use native accounts or SPs in the tenant |
+| **RequestDisallowedByPolicy** | Azure Policy blocking the requested operation | This typically indicates that an Azure Policy is preventing the requested action due to policy restrictions in your subscription For more details and guidance on resolving this issue, refer to: [RequestDisallowedByPolicy](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/create-upgrade-delete/error-code-requestdisallowedbypolicy) |
+| **SpecialFeatureOrQuotaIdRequired** | Subscription lacks access to specific Azure OpenAI models | This error occurs when your subscription does not have access to certain Azure OpenAI models. **Example error message:** `SpecialFeatureOrQuotaIdRequired: The current subscription does not have access to this model 'Format:OpenAI,Name:o3,Version:2025-04-16'.` **Resolution:** To gain access, submit a request using the official form: ๐ [Azure OpenAI Model Access Request](https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR7en2Ais5pxKtso_Pz4b1_xUQ1VGQUEzRlBIMVU2UFlHSFpSNkpOR0paRSQlQCN0PWcu) You'll need to use this form if you require access to the following restricted models: gpt-5 o3 o3-pro deep research reasoning summary gpt-image-1 Once your request is approved, redeploy your resource. |
+| **ResourceProviderError** | Resource provider not registered in subscription | This error occurs when the resource provider is not registered in your subscription To register it, refer to [Register Resource Provider](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-register-resource-provider?tabs=azure-cli) documentation |
+
+--------------------------------
+
+## Quota & Capacity Limitations
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **InternalSubscriptionIsOverQuotaForSku/ ManagedEnvironmentProvisioningError** | Subscription quota exceeded for the requested SKU | Quotas are applied per resource group, subscriptions, accounts, and other scopes. For example, your subscription might be configured to limit the number of vCPUs for a region. If you attempt to deploy a virtual machine with more vCPUs than the permitted amount, you receive an error that the quota was exceeded. For PowerShell, use the `Get-AzVMUsage` cmdlet to find virtual machine quotas: `Get-AzVMUsage -Location "West US"` Based on available quota you can deploy application otherwise, you can request for more quota |
+| **ServiceQuotaExceeded** | Free tier service quota limit reached for Azure AI Search | This error occurs when you attempt to deploy an Azure AI Search service but have already reached the **free tier quota limit** for your subscription. Each Azure subscription is limited to **one free tier Search service**. **Example error message:** `ServiceQuotaExceeded: Operation would exceed 'free' tier service quota. You are using 1 out of 1 'free' tier service quota.` **Common causes:**Already have a free tier Azure AI Search service in the subscription Previous deployment created a free tier Search service that wasn't deleted Attempting to deploy multiple environments with free tier Search services **Resolution:****Option 1: Delete existing free tier Search service:** `az search service list --query "[?sku.name=='free']" -o table` `az search service delete --name --resource-group --yes` **Option 2: Upgrade to a paid SKU:** Modify your Bicep/ARM template to use `basic`, `standard`, or higher SKU instead of `free` **Option 3: Use existing Search service:** Reference the existing free tier Search service in your deployment instead of creating a new one **Request quota increase:** Submit a support request with issue type 'Service and subscription limits (quota)' and quota type 'Search' via [Azure Quota Request](https://aka.ms/AddQuotaSubscription) **Reference:**[Azure AI Search service limits](https://learn.microsoft.com/en-us/azure/search/search-limits-quotas-capacity) [Azure AI Search pricing tiers](https://learn.microsoft.com/en-us/azure/search/search-sku-tier) |
+| **InsufficientQuota** | Not enough quota available in subscription | Check if you have sufficient quota available in your subscription before deployment To verify, refer to the [quota_check](../docs/quota_check.md) file for details |
+| **MaxNumberOfRegionalEnvironmentsInSubExceeded** | Maximum Container App Environments limit reached for region |This error occurs when you attempt to create more **Azure Container App Environments** than the regional quota limit allows for your subscription. Each Azure region has a specific limit on the number of Container App Environments that can be created per subscription. **Common Causes:**Deploying to regions with low quota limits (e.g., Sweden Central allows only 1 environment) Multiple deployments without cleaning up previous environments Exceeding the standard limit of 15 environments in most major regions **Resolution:****Delete unused environments** in the target region, OR **Deploy to a different region** with available capacity, OR **Request quota increase** via [Azure Support](https://go.microsoft.com/fwlink/?linkid=2208872) **Reference:**[Azure Container Apps quotas](https://learn.microsoft.com/en-us/azure/container-apps/quotas) [Azure subscription and service limits](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits) |
+| **SkuNotAvailable** | Requested SKU not available in selected location or zone | This error occurs when the resource SKU you've selected (such as VM size) isn't available for the target location or availability zone. **In this deployment**, the jumpbox VM defaults to `Standard_D2s_v5`. While this size is available in most regions, certain regions or zones may not support it. **Resolution:****Check SKU availability** for your target region: `az vm list-skus --location --size Standard_D2s --output table` **Override the VM size** if the default isn't available in your region: `azd env set AZURE_ENV_VM_SIZE Standard_D2s_v4` **Recommended alternatives** (all support accelerated networking + Premium SSD): - `Standard_D2s_v4` โ previous gen, identical pricing - `Standard_D2as_v5` โ AMD-based, similar pricing - `Standard_D2s_v3` โ older gen, widely available **Avoid A-series VMs** (e.g., `Standard_A2m_v2`) โ they do not support accelerated networking or Premium SSD, which are required by this deployment **Reference:**[Resolve errors for SKU not available](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-sku-not-available) [Azure VM sizes - Dsv5 series](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dsv5-series) |
+| **Conflict - No available instances to satisfy this request** | Azure App Service has insufficient capacity in the region | This error occurs when Azure App Service doesn't have enough available compute instances in the selected region to provision or scale your app. **Common Causes:**High demand in the selected region (e.g., East US, West Europe) Specific SKUs experiencing capacity constraints (Free, Shared, or certain Premium tiers) Multiple rapid deployments in the same region **Resolution:****Wait and Retry** (15-30 minutes): `azd up` **Deploy to a New Resource Group** (Recommended for urgent cases): ``` azd down --force --purge azd up ``` **Try a Different Region:** Update region in `main.bicep` or `azure.yaml` to a less congested region (e.g., `westus2`, `centralus`, `northeurope`) **Use a Different SKU/Tier:** If using Free/Shared tier, upgrade to Basic or Standard Check SKU availability: `az appservice list-locations --sku ` **Reference:** [Azure App Service Plans](https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans) |
+
+--------------------------------
+
+## Resource Group & Deployment Management
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **ResourceGroupNotFound** | Specified resource group does not exist | **Option 1:**Go to [Azure Portal](https://portal.azure.com/#home) Click on **"Resource groups"** option  Search for the resource group in the search bar. If it exists, you can proceed  **Option 2:**This error can occur if you deploy using the same .env file from a previous deployment Create a new environment before redeploying: `azd env new ` |
+| **ResourceGroupBeingDeleted** | Resource group is currently being deleted | **Steps:**Go to [Azure Portal](https://portal.azure.com/#home) Go to resource group option and search for targeted resource group If the resource group is being deleted, you cannot use it. Create a new one or use a different resource group |
+| **DeploymentActive** | Another deployment is already in progress in this resource group | This occurs when a deployment is already in progress and another deployment is triggered in the same resource group Cancel the ongoing deployment before starting a new one Do not initiate a new deployment until the previous one is completed |
+| **DeploymentCanceled** | Deployment was canceled before completion | **Check deployment history:** Go to Azure Portal โ Resource Group โ Deployments Review the detailed error message **Identify the root cause:** Dependent resource failed to deploy Validation error occurred Manual cancellation was triggered **Validate template:** `az deployment group validate --resource-group --template-file main.bicep` **Check resource limits/quotas** **Fix the failed dependency** **Retry deployment:** `az deployment group create --resource-group --template-file main.bicep` ๐ก **Note:** DeploymentCanceled is a wrapper error โ check inner errors in deployment logs |
+| **DeploymentCanceled(user.canceled)** | User manually canceled the deployment | Deployment was manually canceled by the user (Portal, CLI, or pipeline) Check deployment history and logs to confirm who/when it was canceled If accidental, retry the deployment For pipelines, ensure no automation or timeout is triggering cancellation Use deployment locks or retry logic to prevent accidental cancellations |
+| **DeploymentNotFound** | Deployment record not found or was deleted | This occurs when the user deletes a previous deployment along with the resource group, then redeploys the same RG with the same environment name but in a different location Do not change the location when redeploying a deleted RG, OR Use new names for the RG and environment during redeployment |
+| **ResourceGroupDeletionTimeout** | Resource group deletion exceeded timeout limit | Some resources may be stuck deleting or have dependencies; check RG resources and status Ensure no resource locks or Azure Policies are blocking deletion Retry deletion via CLI/PowerShell: `az group delete --name --yes --no-wait` Check Activity Log to identify failing resources Escalate to Azure Support if deletion is stuck |
+
+--------------------------------
+
+## Regional & Location Issues
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **LocationNotAvailableForResourceType** | Resource type not supported in selected region | This error occurs when you attempt to deploy a resource to a region that does not support that specific resource type or SKU. **Resolution:****Verify resource availability by region:** `az provider show --namespace --query "resourceTypes[?resourceType==''].locations" -o table` **Check Azure Products by Region:** [Azure Products by Region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/) **Supported regions for this deployment:**`australiaeast` `centralus` `eastasia` `eastus2` `japaneast` `northeurope` `southeastasia` `uksouth` **Redeploy:** `azd up` |
+| **InvalidResourceLocation** | Cannot change region for already deployed resources | This error occurs when you attempt to modify the location/region of a resource that has already been deployed. Azure resources **cannot change regions** after creation. **Resolution:****Option 1: Delete and Redeploy:** `azd down --force --purge` after purge redeploy app `azd up` **Option 2: Create new environment with different region:** `azd env new ` `azd env set AZURE_LOCATION ` `azd up` **Option 3: Keep existing deployment:** Revert configuration files to use the original region โ ๏ธ **Important:** Backup critical data before deleting resources. **Reference:** [Move Azure resources across regions](https://learn.microsoft.com/en-us/azure/resource-mover/overview) |
+| **ServiceUnavailable/ResourceNotFound** | Service unavailable or restricted in selected region | Regions are restricted to guarantee compatibility with paired regions and replica locations for data redundancy and failover scenarios based on articles [Azure regions list](https://learn.microsoft.com/en-us/azure/reliability/regions-list) and [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions) You can request more quota, refer [Quota Request](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/create-support-request-quota-increase) Documentation |
+| **ResourceOperationFailure/ ProvisioningDisabled** | Resource provisioning restricted or disabled in region | This error occurs when provisioning of a resource is restricted in the selected region. It usually happens because the service is not available in that region or provisioning has been temporarily disabled Regions are restricted to guarantee compatibility with paired regions and replica locations for data redundancy and failover scenarios based on articles [Azure regions list](https://learn.microsoft.com/en-us/azure/reliability/regions-list) and [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions) If you need to use the same region, you can request a quota or provisioning exception. Refer [Quota Request](https://docs.microsoft.com/en-us/azure/sql-database/quota-increase-request) for more details |
+| **RedundancyConfigurationNotAvailableInRegion** | Redundancy configuration not supported in selected region | This issue happens when you try to create a **Storage Account** with a redundancy configuration (e.g., `Standard_GRS`) that is **not supported in the selected Azure region** Example: Creating a storage account with **GRS** in **italynorth** will fail with error: `az storage account create -n mystorageacct123 -g myResourceGroup -l italynorth --sku Standard_GRS --kind StorageV2` To check supported SKUs for your region: `az storage account list-skus -l italynorth -o table` Use a supported redundancy option (e.g., Standard_LRS) in the same region or deploy the Storage Account in a region that supports your chosen redundancy For more details, refer to [Azure Storage redundancy documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-redundancy?utm_source=chatgpt.com) |
+| **NoRegisteredProviderFound** | Unsupported API version for resource type in specified location | This error occurs when you attempt to deploy an Azure resource using an **API version that is not supported** for the specified resource type and location. **Example error message:** `NoRegisteredProviderFound: No registered resource provider found for location 'westeurope' and API version '2020-06-30' for type 'searchServices'. The supported api-versions are '2014-07-31-Preview, 2015-02-28, 2015-08-19, 2019-10-01-Preview, 2020-03-13, 2020-08-01, 2020-08-01-Preview, 2021-04-01-Preview, 2021-06-06-Preview, 2022-09-01, 2023-11-01, 2024-03-01-Preview, 2024-06-01-Preview, 2025-02-01-Preview, 2025-05-01'.` **Common causes:**Using an outdated or invalid API version in Bicep/ARM templates Referencing an Azure Verified Module (AVM) that uses a deprecated API version Copy-pasting old template code with legacy API versions The API version was never valid (typo or incorrect version number) **Resolution:****Update the API version** in your Bicep/ARM template to a supported version listed in the error message. For example, change: `resource searchService 'Microsoft.Search/searchServices@2020-06-30'` to: `resource searchService 'Microsoft.Search/searchServices@2025-05-01'` **Check supported API versions** for a resource type: `az provider show --namespace Microsoft.Search --query "resourceTypes[?resourceType=='searchServices'].apiVersions" -o table` **Use the latest stable API version** when possible (avoid preview versions for production) **Update Azure Verified Modules (AVM)** to their latest versions if using external modules **Validate your template** before deployment: `az deployment group validate --resource-group --template-file main.bicep` **Reference:**[Azure Resource Manager API versions](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-providers-and-types) [Azure AI Search REST API versions](https://learn.microsoft.com/en-us/azure/search/search-api-versions) |
+
+--------------------------------
+
+## Resource Naming & Validation
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **ResourceNameInvalid** | Resource name violates naming convention rules | Ensure the resource name is within the allowed length and naming rules defined for that specific resource type, you can refer [Resource Naming Convention](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules) document |
+| **Workspace Name - InvalidParameter** | Workspace name does not meet required format | To avoid this errors in workspace ID follow below rules:Must start and end with an alphanumeric character (letter or number) Allowed characters: `aโz`, `0โ9`, `- (hyphen)` Cannot start or end with a hyphen - No spaces, underscores (_), periods (.), or special characters Must be unique within the Azure region & subscription Length: 3โ33 characters (for AML workspaces) |
+| **VaultNameNotValid** | Key Vault name does not meet naming requirements | In this template Vault name will be unique everytime, but if you trying to hard code the name then please make sure below points:**Check name length** - Ensure the Key Vault name is between 3 and 24 characters **Validate allowed characters** - The name can only contain letters (aโz, AโZ) and numbers (0โ9). Hyphens are allowed, but not at the beginning or end, and not consecutive (--) **Ensure proper start and end** - The name must start with a letter. The name must end with a letter or digit (not a hyphen) **Test with a new name** - Example of a valid vault name: โ
`cartersaikeyvault1`, โ
`securevaultdemo`, โ
`kv-project123` |
+| **BadRequest: Dns record under zone Document is already taken** | DNS record name already in use | This error can occur only when user hardcoding the CosmosDB Service name. To avoid this you can try few below suggestions:Verify resource names are globally unique If you already created an account/resource with same name in another subscription or resource group, check and delete it before reusing the name By default in this template we are using unique prefix with every resource/account name to avoid this kind for errors |
+
+---------------------------------
+
+## Resource Identification & References
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **LinkedInvalidPropertyId/ ResourceNotFound/ DeploymentOutputEvaluationFailed/ CanNotRestoreANonExistingResource/ The language expression property array index is out of bounds** | Invalid or non-existent resource ID reference | Before using any resource ID, ensure it follows the correct format Verify that the resource ID you are passing actually exists Make sure there are no typos in the resource ID Verify that the provisioning state of the existing resource is `Succeeded` by running the following command to avoid this error while deployment or restoring the resource: `az resource show --ids --query "properties.provisioningState"` Sample Resource IDs format: Log Analytics Workspace Resource ID: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}` Azure AI Foundry Project Resource ID: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.MachineLearningServices/workspaces/{name}` You may encounter the error `The language expression property array index '8' is out of bounds` if the resource ID is incomplete. Please ensure your resource ID is correct and contains all required information, as shown in sample resource IDs For more information refer [Resource Not Found errors solutions](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-not-found?tabs=bicep) |
+| **ParentResourceNotFound** | Parent resource does not exist or cannot be found | You can refer to the [Parent Resource Not found](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-parent-resource?tabs=bicep) documentation if you encounter this error |
+| **PrincipalNotFound** | Principal ID does not exist in Azure AD tenant | This error occurs when the **principal ID** (Service Principal, User, or Group) specified in a role assignment or deployment does not exist in the Azure Active Directory tenant. It can also happen due to **replication delays** right after creating a new principal. **Example causes:**The specified **Object ID** is invalid or belongs to another tenant The principal was recently created but Azure AD has not yet replicated it Attempting to assign a role to a non-existing or deleted Service Principal/User/Group **How to fix:**Verify that the **principal ID is correct** and exists in the same directory/tenant: `az ad sp show --id ` If the principal was just created, wait a few minutes and retry Explicitly set the principalType property (ServicePrincipal, User, or Group) in your ARM/Bicep template to avoid replication delays If the principal does not exist, create it again before assigning roles For more details, see [Azure PrincipalType documentation](https://learn.microsoft.com/en-us/azure/role-based-access-control/troubleshooting?tabs=bicep) |
+| **SubscriptionDoesNotHaveServer** | Referenced SQL Server does not exist in subscription | This issue happens when you try to reference an **Azure SQL Server** (`Microsoft.Sql/servers`) that does not exist in the selected subscription. **It can occur if:**The SQL server name is typed incorrectly The SQL server was **deleted** but is still being referenced You are working in the **wrong subscription context** The server exists in a **different subscription/tenant** where you don't have access **Reproduce:** Run an Azure CLI command with a non-existent server name: `az sql db list --server sql-doesnotexist --resource-group myResourceGroup` or `az sql server show --name sql-caqfrhxr4i3hyj --resource-group myResourceGroup` **Resolution:**Verify the SQL Server name exists in your subscription: `az sql server list --output table` Make sure you are targeting the correct subscription: `az account show` `az account set --subscription ` If the server was deleted, either restore it (if possible) or update references to use a valid existing server |
+
+---------------------------------
+
+## Network & Infrastructure Configuration
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **NetcfgSubnetRangeOutsideVnet** | Subnet IP range outside virtual network address space | Ensure the subnet's IP address range falls within the virtual network's address space Always validate that the subnet CIDR block is a subset of the VNet range For Azure Bastion, the AzureBastionSubnet must be at least /27 Confirm that the AzureBastionSubnet is deployed inside the VNet |
+| **DisableExport_PublicNetworkAccessMustBeDisabled** | Public network access must be disabled when export is disabled | **Check container source:** Confirm whether the deployment is using a Docker image or Azure Container Registry (ACR) **Verify ACR configuration:** If ACR is included, review its settings to ensure they comply with Azure requirements **Check export settings:** If export is disabled in ACR, make sure public network access is also disabled **Redeploy after fix:** Correct the configuration and redeploy. This will prevent the Conflict error during deployment For more information refer [ACR Data Loss Prevention](https://learn.microsoft.com/en-us/azure/container-registry/data-loss-prevention) document |
+| **VMSizeIsNotPermittedToEnableAcceleratedNetworking** | VM size does not support accelerated networking | This error occurs when you attempt to enable accelerated networking on a VM size that does not support it. **Note:** This solution uses `Standard_D2s_v5` which **fully supports accelerated networking**, so this error should not occur with the default configuration. **How to reproduce:**Create or deploy a VM (e.g., via ARM/Bicep) with an unsupported size like `Standard_A2m_v2` or `Standard_B2ms` In the network interface configuration, set `"enableAcceleratedNetworking": true` Submit the request โ Azure throws `VMSizeIsNotPermittedToEnableAcceleratedNetworking` **Resolution:**Use a supported VM size that supports accelerated networking (e.g., `Standard_D2s_v5`, `Standard_D2s_v4`, `Standard_DS2_v2`) Check the [Microsoft list of supported VM sizes for accelerated networking](https://learn.microsoft.com/en-us/azure/virtual-network/accelerated-networking-overview#supported-vm-instances) Alternatively, disable accelerated networking in the NIC configuration if the feature is not required |
+**NetworkSecurityGroupNotCompliantForAzureBastionSubnet** / **SecurityRuleParameterContainsUnsupportedValue** | NSG rules blocking required Azure Bastion ports | This error occurs when the Network Security Group (NSG) attached to `AzureBastionSubnet` explicitly denies inbound TCP ports 443 and/or 4443, which Azure Bastion requires for management and tunneling. **How to reproduce:**Deploy the template with `enablePrivateNetworking=true` so the virtualNetwork module creates `AzureBastionSubnet` and a Network Security Group that denies ports 443 and 4443 Attempt to deploy Azure Bastion into that subnet During validation, Bastion detects the deny rules and fails with `NetworkSecurityGroupNotCompliantForAzureBastionSubnet` **Resolution:** Allow inbound TCP 443 and 4443 on `AzureBastionSubnet` by updating or removing the NSG deny rules Alternatively, deploy Bastion to a subnet without restrictive NSG rules For more details, refer to [Azure Bastion NSG requirements](https://learn.microsoft.com/en-us/azure/bastion/bastion-nsg) |
+| **RouteTableCannotBeAttachedForAzureBastionSubnet** | Route table attached to Azure Bastion subnet | This error occurs because Azure Bastion subnet (`AzureBastionSubnet`) has a platform restriction that prevents route tables from being attached. **How to reproduce:**In `virtualNetwork.bicep`, add `attachRouteTable: true` to the `AzureBastionSubnet` configuration: `{ name: 'AzureBastionSubnet', addressPrefixes: ['10.0.10.0/26'], attachRouteTable: true }` Add a Route Table module to the template Update subnet creation to attach route table conditionally: `routeTableResourceId: subnet.?attachRouteTable == true ? routeTable.outputs.resourceId : null` Deploy the template โ Azure throws `RouteTableCannotBeAttachedForAzureBastionSubnet` **Resolution:**Remove the `attachRouteTable: true` flag from `AzureBastionSubnet` configuration Ensure no route table is associated with `AzureBastionSubnet` Route tables can only be attached to other subnets, not `AzureBastionSubnet` For more details, refer to [Azure Bastion subnet requirements](https://learn.microsoft.com/en-us/azure/bastion/configuration-settings#subnet) |
+
+---------------------------------
-
-ReadOnlyDisabledSubscription
+## Configuration & Property Errors
+
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **InvalidRequestContent** | Deployment contains unrecognized or missing required values | The deployment values either include values that aren't recognized, or required values are missing. Confirm the values for your resource type You can refer [Invalid Request Content error](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/common-deployment-errors#:~:text=InvalidRequestContent,Template%20reference) documentation |
+| **Conflict - Cannot use the SKU Basic with File Change Audit for site** | File Change Audit not supported on Basic SKU | This error happens because File Change Audit logs aren't supported on Basic SKU App Service Plans Upgrading to Premium/Isolated SKU (supports File Change Audit), or Disabling File Change Audit in Diagnostic Settings if you must stay on Basic Always cross-check the [supported log types](https://aka.ms/supported-log-types) before adding diagnostic logs to your Bicep templates |
+| **AccountPropertyCannotBeUpdated** | Read-only property cannot be modified after creation | The property **`isHnsEnabled`** (Hierarchical Namespace for Data Lake Gen2) is **read-only** and can only be set during **storage account creation**. Once a storage account is created, this property **cannot be updated**. Trying to update it via ARM template, Bicep, CLI, or Portal will fail. **Resolution:**Create a **new storage account** with `isHnsEnabled=true` if you require hierarchical namespace Migration may be needed if you already have data Refer to [Storage Account Update Restrictions](https://aka.ms/storageaccountupdate) for more details |
+| **Conflict - Local authentication is disabled** | App Configuration store has local authentication disabled but application is using local auth mode | This error occurs when your Azure App Configuration store has **local authentication disabled** (`disableLocalAuth: true`) but your application is trying to access it using **connection strings or access keys** instead of **Azure AD/Managed Identity authentication**. **Example error message:** `The operation cannot be performed because the configuration store is using local authentication mode and local authentication is disabled. To enable access to data plane resources while local authentication is disabled, please use pass-through authentication mode.` **Common causes:**App Configuration store deployed with `disableLocalAuth: true` for security compliance Application code using connection strings instead of Managed Identity SDK client initialized with access keys rather than `DefaultAzureCredential` **Resolution:****Option 1: Update application to use Managed Identity (Recommended)** ```python from azure.identity import DefaultAzureCredential from azure.appconfiguration import AzureAppConfigurationClient credential = DefaultAzureCredential() client = AzureAppConfigurationClient( endpoint="https://your-appconfig.azconfig.io", credential=credential ) ``` **Option 2: Re-enable local authentication (Not recommended for production)** Set `disableLocalAuth: false` in your Bicep/ARM template **Ensure proper RBAC assignment:** Verify that the Managed Identity has `App Configuration Data Reader` or `App Configuration Data Owner` role assigned **Reference:**[Disable local authentication in Azure App Configuration](https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-disable-access-key-authentication) [Use Managed Identities to access App Configuration](https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-integrate-azure-managed-service-identity) |
+| **PropertyChangeNotAllowed** | Immutable VM property cannot be changed after creation | This error occurs when you attempt to modify an immutable property (such as `osProfile.adminUsername`) on an existing VM. **Cause (Azure Limitation):** Once a VM is created, the `osProfile.adminUsername` property is immutable and cannot be changed. If you modify the VM username or password in the template and redeploy, this issue will occur. **Resolution:**Delete the existing deployment and redeploy with new credentials: `azd down --force --purge` Set new credentials before redeployment: `azd env set AZURE_ENV_VM_ADMIN_USERNAME "newusername"` `azd env set AZURE_ENV_VM_ADMIN_PASSWORD "NewSecurePassword123!"` Redeploy: `azd up` **Note:** Some VM properties are immutable by design. Always plan credential and configuration changes before initial deployment. |
-- Check if you have an active subscription before starting the deployment.
-
-
-
-
- MissingSubscriptionRegistration/ AllowBringYourOwnPublicIpAddress
-
-
-Enable `AllowBringYourOwnPublicIpAddress` Feature
-
-Before deploying the resources, you may need to enable the **Bring Your Own Public IP Address** feature in Azure. This is required only once per subscription.
-
-### Steps
-
-1. **Run the following command to register the feature:**
-
- ```bash
- az feature register --namespace Microsoft.Network --name AllowBringYourOwnPublicIpAddress
- ```
-
-2. **Wait for the registration to complete.**
- You can check the status using:
-
- ```bash
- az feature show --namespace Microsoft.Network --name AllowBringYourOwnPublicIpAddress --query properties.state
- ```
-
-3. **The output should show:**
- "Registered"
-
-4. **Once the feature is registered, refresh the provider:**
-
- ```bash
- az provider register --namespace Microsoft.Network
- ```
-
- ๐ก Note: Feature registration may take several minutes to complete. This needs to be done only once per Azure subscription.
-
-
-
-
-ResourceGroupNotFound
-
-## Option 1
-### Steps
-
-1. Go to [Azure Portal](https:/portal.azure.com/#home).
-
-2. Click on the **"Resource groups"** option available on the Azure portal home page.
-
-
-3. In the Resource Groups search bar, search for the resource group you intend to target for deployment. If it exists, you can proceed with using it.
-
-
- ## Option 2
-
-- This error can occur if you deploy the template using the same .env file - from a previous deployment.
-- To avoid this issue, create a new environment before redeploying.
-- You can use the following command to create a new environment:
- ```
- azd env new
- ```
-
-
-ResourceGroupBeingDeleted
-
-To prevent this issue, please ensure that the resource group you are targeting for deployment is not currently being deleted. You can follow steps to verify resource group is being deleted or not.
-### Steps:
-1. Go to [Azure Portal](https://portal.azure.com/#home)
-2. Go to resource group option and search for targeted resource group
-3. If Targeted resource group is there and deletion for this is in progress, it means u cannot use this, you can create new or use any other resource group
-
-
-
-
-InternalSubscriptionIsOverQuotaForSku/ManagedEnvironmentProvisioningError
-
-Quotas are applied per resource group, subscriptions, accounts, and other scopes. For example, your subscription might be configured to limit the number of vCPUs for a region. If you attempt to deploy a virtual machine with more vCPUs than the permitted amount, you receive an error that the quota was exceeded.
-For PowerShell, use the `Get-AzVMUsage` cmdlet to find virtual machine quotas.
-```ps
-Get-AzVMUsage -Location "West US"
-```
-based on available quota you can deploy application otherwise, you can request for more quota
-
-
-
-InsufficientQuota
-
-- Check if you have sufficient quota available in your subscription before deployment.
-- To verify, refer to the [quota_check](../docs/quota_check.md) file for details.
-
-
-
-
-DeploymentModelNotSupported/ ServiceModelDeprecated/ InvalidResourceProperties
-
- - The updated model may not be supported in the selected region. Please verify its availability in the [Azure AI Foundry models](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/models?tabs=global-standard%2Cstandard-chat-completions) document.
-
-
-
-LinkedInvalidPropertyId/ ResourceNotFound/DeploymentOutputEvaluationFailed/ CanNotRestoreANonExistingResource / The language expression property array index is out of bounds
-
-- Before using any resource ID, ensure it follows the correct format.
-- Verify that the resource ID you are passing actually exists.
-- Make sure there are no typos in the resource ID.
-- Verify that the provisioning state of the existing resource is `Succeeded` by running the following command to avoid this error while deployment or restoring the resource.
-
- ```
- az resource show --ids --query "properties.provisioningState"
- ```
-- Sample Resource IDs format
- - Log Analytics Workspace Resource ID
- ```
- /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}
- ```
- - Azure AI Foundry Project Resource ID
- ```
- /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.MachineLearningServices/workspaces/{name}
- ```
-- You may encounter the error `The language expression property array index '8' is out of bounds` if the resource ID is incomplete. Please ensure your resource ID is correct and contains all required information, as shown in sample resource IDs.
-
-- For more information refer [Resource Not Found errors solutions](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-not-found?tabs=bicep)
-
-
-
-ResourceNameInvalid
-
-- Ensure the resource name is within the allowed length and naming rules defined for that specific resource type, you can refer [Resource Naming Convention](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules) document.
-
-
-
-ServiceUnavailable/ResourceNotFound
-
- - Regions are restricted to guarantee compatibility with paired regions and replica locations for data redundancy and failover scenarios based on articles [Azure regions list](https://learn.microsoft.com/en-us/azure/reliability/regions-list) and [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions).
-
- - You can request more quota, refer [Quota Request](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/create-support-request-quota-increase) Documentation
-
-
-
-
-Workspace Name - InvalidParameter
-
- To avoid this errors in workspace ID follow below rules.
-1. Must start and end with an alphanumeric character (letter or number).
-2. Allowed characters:
- `aโz`
- `0โ9`
- `- (hyphen)`
-3. Cannot start or end with a hyphen -.
-4. No spaces, underscores (_), periods (.), or special characters.
-5. Must be unique within the Azure region & subscription.
-6. Length: 3โ33 characters (for AML workspaces).
-
-
-BadRequest: Dns record under zone Document is already taken
-
-This error can occur only when user hardcoding the CosmosDB Service name. To avoid this you can try few below suggestions.
-- Verify resource names are globally unique.
-- If you already created an account/resource with same name in another subscription or resource group, check and delete it before reusing the name.
-- By default in this template we are using unique prefix with every resource/account name to avoid this kind for errors.
-
-
-NetcfgSubnetRangeOutsideVnet
-
-- Ensure the subnetโs IP address range falls within the virtual networkโs address space.
-- Always validate that the subnet CIDR block is a subset of the VNet range.
-- For Azure Bastion, the AzureBastionSubnet must be at least /27.
-- Confirm that the AzureBastionSubnet is deployed inside the VNet.
-
-
-DisableExport_PublicNetworkAccessMustBeDisabled
-
-- Check container source: Confirm whether the deployment is using a Docker image or Azure Container Registry (ACR).
-- Verify ACR configuration: If ACR is included, review its settings to ensure they comply with Azure requirements.
-- Check export settings: If export is disabled in ACR, make sure public network access is also disabled.
-- Dedeploy after fix: Correct the configuration and redeploy. This will prevent the Conflict error during deployment.
-- For more information refer [ACR Data Loss Prevention](https://learn.microsoft.com/en-us/azure/container-registry/data-loss-prevention) document.
-
-
-AccountProvisioningStateInvalid
-
-- The AccountProvisioningStateInvalid error occurs when you try to use resources while they are still in the Accepted provisioning state.
-- This means the deployment has not yet fully completed.
-- To avoid this error, wait until the provisioning state changes to Succeeded.
-- Only use the resources once the deployment is fully completed.
-
-
-VaultNameNotValid
-
- In this template Vault name will be unique everytime, but if you trying to hard code the name then please make sure below points.
- 1. Check name length
- - Ensure the Key Vault name is between 3 and 24 characters.
- 2. Validate allowed characters
- - The name can only contain letters (aโz, AโZ) and numbers (0โ9).
- - Hyphens are allowed, but not at the beginning or end, and not consecutive (--).
-3. Ensure proper start and end
- - The name must start with a letter.
- - The name must end with a letter or digit (not a hyphen).
-4. Test with a new name
- - Example of a valid vault name:
- โ
`cartersaikeyvault1`
- โ
`securevaultdemo`
- โ
`kv-project123`
-
-
-DeploymentCanceled
-
- There might be multiple reasons for this error you can follow below steps to troubleshoot.
- 1. Check deployment history
- - Go to Azure Portal โ Resource Group โ Deployments.
- - Look at the detailed error message for the deployment that was canceled โ this will show which resource failed and why.
- 2. Identify the root cause
- - A DeploymentCanceled usually means:
- - A dependent resource failed to deploy.
- - A validation error occurred earlier.
- - A manual cancellation was triggered.
- - Expand the failed deployment logs for inner error messages.
-3. Validate your template (ARM/Bicep)
- Run:
- ```
- az deployment group validate --resource-group --template-file main.bicep
- ```
-4. Check resource limits/quotas
- - Ensure you have not exceeded quotas (vCPUs, IPs, storage accounts, etc.), which can silently cause cancellation.
-5. Fix the failed dependency
- - If a specific resource shows BadRequest, Conflict, or ValidationError, resolve that first.
- - Re-run the deployment after fixing the root cause.
-6. Retry deployment
- Once corrected, redeploy with:
- ```
- az deployment group create --resource-group --template-file main.bicep
- ```
-Essentially: DeploymentCanceled itself is just a wrapper error โ you need to check inner errors in the deployment logs to find the actual failure.
-
-
-LocationNotAvailableForResourceType
-
-- You may encounter a LocationNotAvailableForResourceType error if you set the secondary location to 'Australia Central' in the main.bicep file.
-- This happens because 'Australia Central' is not a supported region for that resource type.
-- Always refer to the README file or Azure documentation to check the list of supported regions.
-- Update the deployment with a valid supported region to resolve the issue.
-
-
-
-
-InvalidResourceLocation
-
-- You may encounter an InvalidResourceLocation error if you change the region for Cosmos DB or the Storage Account (secondary location) multiple times in the main.bicep file and redeploy.
-- Azure resources like Cosmos DB and Storage Accounts do not support changing regions after deployment.
-- If you need to change the region again, first delete the existing deployment.
-- Then redeploy the resources with the updated region configuration.
-
-
-
-
-
-DeploymentActive
-
-- This issue occurs when a deployment is already in progress and another deployment is triggered in the same resource group, causing a DeploymentActive error.
-- Cancel the ongoing deployment before starting a new one.
-- Do not initiate a new deployment in the same resource group until the previous one is completed.
-
-
-
-ResourceOperationFailure/ProvisioningDisabled
-
- - This error occurs when provisioning of a resource is restricted in the selected region.
- It usually happens because the service is not available in that region or provisioning has been temporarily disabled.
-
- - Regions are restricted to guarantee compatibility with paired regions and replica locations for data redundancy and failover scenarios based on articles [Azure regions list](https://learn.microsoft.com/en-us/azure/reliability/regions-list) and [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions).
-
-- If you need to use the same region, you can request a quota or provisioning exception.
- Refer [Quota Request](https://docs.microsoft.com/en-us/azure/sql-database/quota-increase-request) for more details.
-
-
-
-
-MaxNumberOfRegionalEnvironmentsInSubExceeded
-
-- This error occurs when you try to create more than the allowed number of **Azure Container App Environments (ACA Environments)** in the same region for a subscription.
-- For example, in **Sweden Central**, only **1 Container App Environment** is allowed per subscription.
-
-The subscription 'xxxx-xxxx' cannot have more than 1 Container App Environments in Sweden Central.
-
-- To fix this, you can:
- - Deploy the Container App Environment in a **different region**, OR
- - Request a quota increase via Azure Support โ [Quota Increase Request](https://go.microsoft.com/fwlink/?linkid=2208872)
-
-
-
-
-Unauthorized - Operation cannot be completed without additional quota
-
-- You can check your quota usage using `az vm list-usage`.
-
- ```
- az vm list-usage --location "" -o table
- ```
-- To Request more quota refer [VM Quota Request](https://techcommunity.microsoft.com/blog/startupsatmicrosoftblog/how-to-increase-quota-for-specific-types-of-azure-virtual-machines/3792394).
-
-
-
-ParentResourceNotFound
-
-- You can refer to the [Parent Resource Not found](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-parent-resource?tabs=bicep) documentation if you encounter this error.
-
-
-
-ResourceProviderError
-
-- This error occurs when the resource provider is not registered in your subscription.
-- To register it, refer to [Register Resource Provider](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-register-resource-provider?tabs=azure-cli) documentation.
-
-
-
-Conflict - Cannot use the SKU Basic with File Change Audit for site.
-
-- This error happens because File Change Audit logs arenโt supported on Basic SKU App Service Plans.
-
-- Upgrading to Premium/Isolated SKU (supports File Change Audit), or
-
-- Disabling File Change Audit in Diagnostic Settings if you must stay on Basic.
-- Always cross-check the [supported log types](https://aka.ms/supported-log-types)
- before adding diagnostic logs to your Bicep templates.
-
-
-
-
-
-AccountPropertyCannotBeUpdated
-
-- The property **`isHnsEnabled`** (Hierarchical Namespace for Data Lake Gen2) is **read-only** and can only be set during **storage account creation**.
-- Once a storage account is created, this property **cannot be updated**.
-- Trying to update it via ARM template, Bicep, CLI, or Portal will fail.
-
-- **Resolution**
-- Create a **new storage account** with `isHnsEnabled=true` if you require hierarchical namespace.
-- Migration may be needed if you already have data.
-- Refer to [Storage Account Update Restrictions](https://aka.ms/storageaccountupdate) for more details.
-
-
-
-InvalidRequestContent
-
-- The deployment values either include values that aren't recognized, or required values are missing. Confirm the values for your resource type.
-- You can refer [Invalid Request Content error](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/common-deployment-errors#:~:text=InvalidRequestContent,Template%20reference) documentation.
-
-
-
-ReadOnlyDisabledSubscription
-
-- Depending on the type of the Azure Subscription, the expiration date might have been reached.
-
-- You have to activate the Azure Subscription before creating any Azure resource.
-- You can refer [Reactivate a disabled Azure subscription](https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/subscription-disabled) Documentation.
-
-
-
-
-SkuNotAvailable
-
-- You receive this error in the following scenarios:
- - When the resource SKU you've selected, such as VM size, isn't available for a location or zone.
- - If you're deploying an Azure Spot VM or Spot scale set instance, and there isn't any capacity for Azure Spot in this location. For more information, see Spot error messages.
-
-
-CrossTenantDeploymentNotPermitted
-
-- Check tenant match: Ensure your deployment identity (user/SP) and the target resource group are in the same tenant.
- ```
- az account show
- az group show --name
- ```
-
-- Verify pipeline/service principal: If using CI/CD, confirm the service principal belongs to the same tenant and has permissions on the resource group.
-
-- Avoid cross-tenant references: Make sure your Bicep doesnโt reference subscriptions, resource groups, or resources in another tenant.
-
-- Test minimal deployment: Deploy a simple resource to the same resource group to confirm identity and tenant are correct.
-
-- Guest/external accounts: Avoid using guest users from other tenants; use native accounts or SPs in the tenant.
-
-
-
-RequestDisallowedByPolicy
-
-- This typically indicates that an Azure Policy is preventing the requested action due to policy restrictions in your subscription.
-
-- For more details and guidance on resolving this issue, please refer to the official Microsoft documentation: [RequestDisallowedByPolicy](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/create-upgrade-delete/error-code-requestdisallowedbypolicy)
-
-
-
-
-FlagMustBeSetForRestore/NameUnavailable/CustomDomainInUse
-
-- This error occurs when you try to deploy a Cognitive Services resource that was **soft-deleted** earlier.
-- Azure requires you to explicitly set the **`restore` flag** to `true` if you want to recover the soft-deleted resource.
-- If you donโt want to restore the resource, you must **purge the deleted resource** first before redeploying.
-Example causes:
-- Trying to redeploy a Cognitive Services account with the same name as a previously deleted one.
-- The deleted resource still exists in a **soft-delete retention state**.
-**How to fix:**
-1. If you want to restore โ add `"restore": true` in your template properties.
-2. If you want a fresh deployment โ purge the resource using:
- ```bash
- az cognitiveservices account purge \
- --name \
- --resource-group \
- --location
- ```
-For more details, refer to [Soft delete and resource restore](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/delete-resource-group?tabs=azure-powershell).
-
-
-
-PrincipalNotFound
-
-- This error occurs when the **principal ID** (Service Principal, User, or Group) specified in a role assignment or deployment does not exist in the Azure Active Directory tenant.
-- It can also happen due to **replication delays** right after creating a new principal.
-**Example causes:**
-- The specified **Object ID** is invalid or belongs to another tenant.
-- The principal was recently created but Azure AD has not yet replicated it.
-- Attempting to assign a role to a non-existing or deleted Service Principal/User/Group.
-**How to fix:**
-1. Verify that the **principal ID is correct** and exists in the same directory/tenant.
- ```bash
- az ad sp show --id
- ```
-2. If the principal was just created, wait a few minutes and retry.
-3. Explicitly set the principalType property (ServicePrincipal, User, or Group) in your ARM/Bicep template to avoid replication delays.
-4. If the principal does not exist, create it again before assigning roles.
-For more details, see [Azure PrincipalType documentation](https://learn.microsoft.com/en-us/azure/role-based-access-control/troubleshooting?tabs=bicep)
-
-
-RedundancyConfigurationNotAvailableInRegion
-
-- This issue happens when you try to create a **Storage Account** with a redundancy configuration (e.g., `Standard_GRS`) that is **not supported in the selected Azure region**.
-- Example: Creating a storage account with **GRS** in **italynorth** will fail with this error.
-```bash
-az storage account create -n mystorageacct123 -g myResourceGroup -l italynorth --sku Standard_GRS --kind StorageV2
-```
-- To check supported SKUs for your region:
-```bash
-az storage account list-skus -l italynorth -o table
-```
-Use a supported redundancy option (e.g., Standard_LRS) in the same region
-Or deploy the Storage Account in a region that supports your chosen redundancy.
-For more details, refer to [Azure Storage redundancy documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-redundancy?utm_source=chatgpt.com).
-
-
- DeploymentNotFound
-
-- This issue occurs when the user deletes a previous deployment along with the resource group (RG), and then redeploys the same RG with the same environment name but in a different location.
-
-- To avoid the DeploymentNotFound error, Do not change the location when redeploying a deleted RG, or Use new names for the RG and environment during redeployment.
-
-
-DeploymentCanceled(user.canceled)
-
-- Indicates the deployment was manually canceled by the user (Portal, CLI, or pipeline).
-
-- Check deployment history and logs to confirm who/when it was canceled.
-
-- If accidental, retry the deployment.
-
-- For pipelines, ensure no automation or timeout is triggering cancellation.
-- Use deployment locks or retry logic to prevent accidental cancellations.
-
+----------------------------------
-ResourceGroupDeletionTimeout
+## Resource State & Provisioning
-- Some resources in the resource group may be stuck deleting or have dependencies; check RG resources and status.
+| Issue/Error Code | Description | Steps to Resolve |
+|-----------------|-------------|------------------|
+| **AccountProvisioningStateInvalid** | Resource used before provisioning completed | The AccountProvisioningStateInvalid error occurs when you try to use resources while they are still in the Accepted provisioning state This means the deployment has not yet fully completed To avoid this error, wait until the provisioning state changes to Succeeded Only use the resources once the deployment is fully completed |
+| **BadRequest - DatabaseAccount is in a failed provisioning state because the previous attempt to create it was not successful** | Database account failed to provision previously | This error occurs when a user attempts to redeploy a resource that previously failed to provision To resolve the issue, delete the failed deployment first, then start a new deployment For guidance on deleting a resource from a Resource Group, refer to the following link: [Delete an Azure Cosmos DB account](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/manage-with-powershell#delete-account:~:text=%3A%24enableMultiMaster-,Delete%20an%20Azure%20Cosmos%20DB%20account,-This%20command%20deletes) |
+| **ServiceDeleting** | Cannot provision service because deletion is still in progress | This error occurs when you attempt to create an Azure Search service with the same name as one that is currently being deleted. Azure Search services have a **soft-delete period** during which the service name remains reserved. **Common causes:**Deleting a Search service and immediately trying to recreate it with the same name Rapid redeployments using the same service name in Bicep/ARM templates The deletion operation is asynchronous and takes several minutes to complete **Resolution:****Wait for deletion to complete** (10-15 minutes) before redeploying **Use a different service name** - append timestamp or unique identifier to the name **Implement retry logic** with exponential backoff as suggested in the error message **Check deletion status** before recreating: `az search service show --name --resource-group ` For Bicep deployments, ensure your naming strategy includes unique suffixes to avoid conflicts For more details, refer to [Azure Search service limits](https://learn.microsoft.com/en-us/azure/search/search-limits-quotas-capacity) |
+| **FailedIdentityOperation / ManagedEnvironmentScheduledForDelete** | Identity operation failed due to pending delete or resource conflict | This error occurs when you attempt to create or update an Azure Container Apps Managed Environment while it has a **pending delete operation** or the resource already exists in a conflicting state. **Example error messages:** `FailedIdentityOperation: Identity operation for resource failed with error 'Failed to perform resource identity operation. Status: 'Conflict'. Response: 'Request specified that resource is new, but resource already exists. This may be due to a pending delete operation, try again later.'` `ManagedEnvironmentScheduledForDelete: The environment 'cae-xxx' is under deletion. Please retry the creation with new name or wait for the deletion completed.` **Common causes:**Deleting a Container Apps Environment and immediately trying to recreate it with the same name Rapid redeployments using `azd up` without waiting for previous cleanup Resource group deletion in progress while attempting to redeploy Previous deployment failed or was canceled, leaving resources in an inconsistent state Concurrent deployments targeting the same resources **Resolution:****Wait for deletion to complete** (5-15 minutes) before redeploying: `az containerapp env show --name --resource-group --query "properties.provisioningState"` **Check environment status:** If status is `ScheduledForDelete` or `Deleting`, wait for it to complete **Use a new environment name:** Create a new environment with a different name or use a new resource group: `azd env new ` `azd up` **Force delete and wait:** If the environment is stuck, try force deletion: `az containerapp env delete --name --resource-group --yes` Wait for deletion to complete before redeploying **Delete associated Container Apps first:** If the environment has apps, delete them before the environment: `az containerapp list --environment --resource-group -o table` `az containerapp delete --name --resource-group --yes` **Use unique naming:** Implement timestamp or unique suffix in your naming strategy to avoid conflicts **Reference:**[Azure Container Apps troubleshooting](https://learn.microsoft.com/en-us/azure/container-apps/troubleshooting) [Manage Container Apps environments](https://learn.microsoft.com/en-us/azure/container-apps/environment) |
+| **BadRequest - Parent account does not provision correctly** | Parent AI Services/Cognitive Services account failed to provision | This error occurs when a **child resource** (such as an AI project, model deployment, or other dependent resource) attempts to be created on a **parent Cognitive Services/AI Services account** that has **failed to provision** or is in an incomplete state. **Example error message:** `Parent account does not provision correctly, please retry creating the account.` **Common causes:**Parent AI Services account provisioning failed due to quota, region, or configuration issues Using `restore: true` flag when no soft-deleted resource exists to restore Network or transient errors during parent account creation Invalid configuration on the parent account (e.g., invalid SKU, unsupported region) Previous deployment of the parent account was interrupted or canceled **Resolution:****Check parent account status:** `az cognitiveservices account show --name --resource-group --query "properties.provisioningState"` **Delete failed parent account and redeploy:** `az cognitiveservices account delete --name --resource-group ` Then run: `azd up` **If using restore flag incorrectly:** Ensure `restore: false` in your Bicep template unless you specifically need to restore a soft-deleted resource **Check for soft-deleted resources:** `az cognitiveservices account list-deleted` **Purge soft-deleted resources if needed:** `az cognitiveservices account purge --name --resource-group --location ` **Verify quota and region availability:** Ensure you have sufficient quota and the service is available in your selected region **Reference:**[Manage Cognitive Services accounts](https://learn.microsoft.com/en-us/azure/ai-services/manage-resources) [Recover deleted Cognitive Services resources](https://learn.microsoft.com/en-us/azure/ai-services/recover-purge-resources) |
+---------------------------------
-- Ensure no resource locks or Azure Policies are blocking deletion.
+## Miscellaneous
-- Retry deletion via CLI/PowerShell `(az group delete --name --yes --no-wait)`.
+| Issue/Error Code | Description | Steps to Resolve |
+|-------------|-------------|------------------|
+| **DeploymentModelNotSupported/ ServiceModelDeprecated/ InvalidResourceProperties** | Model not supported or deprecated in selected region | The updated model may not be supported in the selected region. Please verify its availability in the [Azure AI Foundry models](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/models?tabs=global-standard%2Cstandard-chat-completions) document |
+| **FlagMustBeSetForRestore/ NameUnavailable/ CustomDomainInUse** | Soft-deleted resource requires restore flag or purge | This error occurs when you try to deploy a Cognitive Services resource that was **soft-deleted** earlier. Azure requires you to explicitly set the **`restore` flag** to `true` if you want to recover the soft-deleted resource. If you don't want to restore the resource, you must **purge the deleted resource** first before redeploying. **Example causes:**Trying to redeploy a Cognitive Services account with the same name as a previously deleted one The deleted resource still exists in a **soft-delete retention state** **How to fix:**If you want to restore โ add `"restore": true` in your template properties If you want a fresh deployment โ purge the resource using: `az cognitiveservices account purge --name --resource-group --location ` For more details, refer to [Soft delete and resource restore](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/delete-resource-group?tabs=azure-powershell) |
+| **ContainerAppOperationError** | Container image build or deployment issue | The error is likely due to an improperly built container image. For resolution steps, refer to the [Azure Container Registry (ACR) โ Build & Push Guide](./ACRBuildAndPushGuide.md) |
+| **LinkedAuthorizationFailed** | Service principal lacks permission to use a linked resource required for deployment | This error occurs when a service principal doesn't have permission to perform an action on a linked resource that is required for the operation (e.g., cluster creation). **Common causes:**The service principal has permission on the primary resource but lacks permission on the linked scope Missing role assignment for operations like `Microsoft.Network/ddosProtectionPlans/join/action` **Resolution:**Identify the **service principal**, **resource**, and **operation** from the error message Grant the service principal the required permissions on the linked resource Use [Assign Azure roles using the Azure portal](https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal) to add the role assignment For more details, refer to [LinkedAuthorizationFailed error](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/error-codes/linkedauthorizationfailed-error) |
+| **ContainerOperationFailure** | Container image or storage resource does not exist | This error occurs when an operation fails because the **specified container resource does not exist**. This can happen with Azure Container Registry images or Azure Storage blob containers. **Example error message:** `ContainerOperationFailure: The specified resource does not exist. RequestId:xxxxx Time:xxxxx` **Common causes:****Invalid container image tag:** The specified image tag does not exist in the container registry **Non-existent container registry:** The container registry endpoint is incorrect or inaccessible **Missing blob container:** The storage blob container referenced by the application does not exist **Incorrect storage account URL:** The storage account endpoint is misconfigured **Permission issues:** The managed identity lacks permissions to access the container registry or storage account **Resolution:****Verify container image exists:** `az acr repository show-tags --name --repository ` **Check image tag in deployment:** Ensure the `imageTag` parameter matches an existing tag in the registry **Verify storage containers exist:** `az storage container list --account-name --auth-mode login` **Check role assignments:** Ensure the Container App's managed identity has `AcrPull` role on the container registry and `Storage Blob Data Contributor` role on the storage account **Rebuild and push container images:** If images are missing, follow the [ACR Build & Push Guide](./ACRBuildAndPushGuide.md) **Verify storage account URL:** Ensure `APP_STORAGE_BLOB_URL` and `APP_STORAGE_QUEUE_URL` in App Configuration point to the correct storage account **Reference:**[Azure Container Registry troubleshooting](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-troubleshoot-login) [Azure Storage troubleshooting](https://learn.microsoft.com/en-us/azure/storage/common/storage-troubleshoot-common-errors) |
-- Check Activity Log to identify failing resources; escalate to Azure Support if deletion is stuck.
-
-
-
-SubscriptionDoesNotHaveServer
-
-- This issue happens when you try to reference an **Azure SQL Server** (`Microsoft.Sql/servers`) that does not exist in the selected subscription.
-- It can occur if:
- - The SQL server name is typed incorrectly.
- - The SQL server was **deleted** but is still being referenced.
- - You are working in the **wrong subscription context**.
- - The server exists in a **different subscription/tenant** where you donโt have access.
-
-**Reproduce:**
-1. Run an Azure CLI command with a non-existent server name:
-```bash
- az sql db list --server sql-doesnotexist --resource-group myResourceGroup
-```
-
- or
-
-```bash
- az sql server show --name sql-caqfrhxr4i3hyj --resource-group myResourceGroup
-
-```
-
-Resolution:
-
-Verify the SQL Server name exists in your subscription:
-
-```bash
- az sql server list --output table
-```
-Make sure you are targeting the correct subscription:
-
-```bash
- az account show
- az account set --subscription
-```
-If the server was deleted, either restore it (if possible) or update references to use a valid existing server.
-
-
-
-
-DeploymentCanceled(user.canceled)
-
-- Indicates the deployment was manually canceled by the user (Portal, CLI, or pipeline).
-
-- Check deployment history and logs to confirm who/when it was canceled.
-
-- If accidental, retry the deployment.
-
-- For pipelines, ensure no automation or timeout is triggering cancellation.
-
-- Use deployment locks or retry logic to prevent accidental cancellations.
-
-
-
-BadRequest - DatabaseAccount is in a failed provisioning state because the previous attempt to create it was not successful
-
-- This error occurs when a user attempts to redeploy a resource that previously failed to provision.
-
-- To resolve the issue, delete the failed deployment first, then start a new deployment.
-
-- For guidance on deleting a resource from a Resource Group, refer to the following link: [Delete an Azure Cosmos DB account](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/manage-with-powershell#delete-account:~:text=%3A%24enableMultiMaster-,Delete%20an%20Azure%20Cosmos%20DB%20account,-This%20command%20deletes)
-
-
-
-
-
-SpecialFeatureOrQuotaIdRequired
-
-This error occurs when your subscription does not have access to certain Azure OpenAI models.
-
-**Example error message:**
-`SpecialFeatureOrQuotaIdRequired: The current subscription does not have access to this model 'Format:OpenAI,Name:o3,Version:2025-04-16'.`
-
-**Resolution:**
-To gain access, submit a request using the official form:
-๐ [Azure OpenAI Model Access Request](https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR7en2Ais5pxKtso_Pz4b1_xUQ1VGQUEzRlBIMVU2UFlHSFpSNkpOR0paRSQlQCN0PWcu)
-
-Youโll need to use this form if you require access to the following restricted models:
-- gpt-5
-- o3
-- o3-pro
-- deep research
-- reasoning summary
-- gpt-image-1
-
-Once your request is approved, redeploy your resource.
-
-
-
-
-ContainerAppOperationError
-
-- The error is likely due to an improperly built container image. For resolution steps, refer to the [Azure Container Registry (ACR) โ Build & Push Guide](./ACRBuildAndPushGuide.md)
-
-
+---------------------------------
๐ก Note: If you encounter any other issues, you can refer to the [Common Deployment Errors](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/common-deployment-errors) documentation.
If the problem persists, you can also raise an bug in our [MACAE Github Issues](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator/issues) for further support.
diff --git a/docs/docker_mcp_server_testing.md b/docs/docker_mcp_server_testing.md
index 62d41b654..ecc116a89 100644
--- a/docs/docker_mcp_server_testing.md
+++ b/docs/docker_mcp_server_testing.md
@@ -13,7 +13,7 @@ This document provides comprehensive steps to test the MACAE MCP Server deployed
```bash
# Navigate to MCP server directory
-cd src/backend/v3/mcp_server
+cd src/backend/v4/mcp_server
# Build and run in one command
docker build -t macae-mcp-server . && docker run -d --name macae-mcp-server -p 9000:9000 macae-mcp-server python mcp_server.py --transport http --host 0.0.0.0 --port 9000
@@ -25,7 +25,7 @@ docker build -t macae-mcp-server . && docker run -d --name macae-mcp-server -p 9
```bash
# Navigate to the MCP server directory
-cd c:\workstation\Microsoft\github\MACAE_ME\src\backend\v3\mcp_server
+cd c:\workstation\Microsoft\github\MACAE_ME\src\backend\v4\mcp_server
# Build the Docker image
docker build -t macae-mcp-server:latest .
diff --git a/docs/images/Deployment_success_message.png b/docs/images/Deployment_success_message.png
new file mode 100644
index 000000000..679ead4da
Binary files /dev/null and b/docs/images/Deployment_success_message.png differ
diff --git a/docs/images/Environment_varibles_example.png b/docs/images/Environment_varibles_example.png
new file mode 100644
index 000000000..47e78a14e
Binary files /dev/null and b/docs/images/Environment_varibles_example.png differ
diff --git a/docs/images/Usecase_selection.png b/docs/images/Usecase_selection.png
new file mode 100644
index 000000000..506929409
Binary files /dev/null and b/docs/images/Usecase_selection.png differ
diff --git a/docs/images/mcpContainer.png b/docs/images/mcpContainer.png
new file mode 100644
index 000000000..d72ba24e5
Binary files /dev/null and b/docs/images/mcpContainer.png differ
diff --git a/docs/images/readme/agent_flow.png b/docs/images/readme/agent_flow.png
index 1ba7c9fe6..fd22ae775 100644
Binary files a/docs/images/readme/agent_flow.png and b/docs/images/readme/agent_flow.png differ
diff --git a/docs/images/readme/architecture.png b/docs/images/readme/architecture.png
index 12304dda5..b4d5976b6 100644
Binary files a/docs/images/readme/architecture.png and b/docs/images/readme/architecture.png differ
diff --git a/docs/images/vscodeweb_intialize.png b/docs/images/vscodeweb_intialize.png
new file mode 100644
index 000000000..1ef8ce817
Binary files /dev/null and b/docs/images/vscodeweb_intialize.png differ
diff --git a/docs/quota_check.md b/docs/quota_check.md
index 61a70dbbd..6455e0629 100644
--- a/docs/quota_check.md
+++ b/docs/quota_check.md
@@ -5,17 +5,22 @@ Before deploying the accelerator, **ensure sufficient quota availability** for t
### Login if you have not done so already
```
-azd auth login
+az login
+```
+
+If using VS Code Web:
+```
+az login --use-device-code
```
### ๐ Default Models & Capacities:
```
-gpt-4o:150
+gpt4.1:150,o4-mini:50,gpt4.1-mini:50
```
### ๐ Default Regions:
```
-eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southcentralus, canadacentral
+australiaeast, eastus2, francecentral, japaneast, norwayeast, swedencentral, uksouth, westus
```
### Usage Scenarios:
- No parameters passed โ Default models and capacities will be checked in default regions.
@@ -37,19 +42,23 @@ eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southc
```
โ๏ธ Check specific model(s) in default regions:
```
- ./quota_check_params.sh --models gpt-4o:150
+ ./quota_check_params.sh --models gpt4.1:150
```
โ๏ธ Check default models in specific region(s):
```
-./quota_check_params.sh --regions eastus,westus
+./quota_check_params.sh --regions eastus2,westus
```
โ๏ธ Passing Both models and regions:
```
- ./quota_check_params.sh --models gpt-4o:150 --regions eastus,westus2
+ ./quota_check_params.sh --models gpt4.1:150 --regions eastus2,westus
```
โ๏ธ All parameters combined:
```
- ./quota_check_params.sh --models gpt-4o:150 --regions eastus,westus --verbose
+ ./quota_check_params.sh --models gpt4.1:150 --regions eastus2,westus --verbose
+ ```
+โ๏ธ Multiple models with single region:
+ ```
+ ./quota_check_params.sh --models gpt4.1:150,gpt4.1-mini:50 --regions eastus2 --verbose
```
### **Sample Output**
@@ -75,7 +84,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`.
+2. If you're using VS Code, click the dropdown on the right side of the terminal window, and select `Git Bash` / `bash`.

3. Navigate to the `scripts` folder where the script files are located and make the script as executable:
```sh
@@ -98,4 +107,5 @@ The final table lists regions with available quota. You can select any of these
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
az login
```
+ > Note: Use `az login --use-device-code` in VS Code Web.
6. Rerun the script after installing Azure CLI.
diff --git a/infra/main.bicep b/infra/main.bicep
index e1fa63250..2b4868715 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -67,12 +67,15 @@ param gpt4_1ModelVersion string = '2025-04-14'
@description('Optional. Name of the GPT Reasoning model to deploy:')
param gptReasoningModelName string = 'o4-mini'
-@description('Optional. Version of the GPT Reasoning model to deploy. Defaults to 2025-04-14.')
+@description('Optional. Version of the GPT Reasoning model to deploy. Defaults to 2025-04-16.')
param gptReasoningModelVersion string = '2025-04-16'
-@description('Optional. Version of the Azure OpenAI service to deploy. Defaults to 2025-01-01-preview.')
+@description('Optional. Version of the Azure OpenAI service to deploy. Defaults to 2024-12-01-preview.')
param azureopenaiVersion string = '2024-12-01-preview'
+@description('Optional. Version of the Azure AI Agent API version. Defaults to 2025-01-01-preview.')
+param azureAiAgentAPIVersion string = '2025-01-01-preview'
+
@minLength(1)
@allowed([
'Standard'
@@ -123,11 +126,14 @@ param enablePrivateNetworking bool = false
@secure()
@description('Optional. The user name for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true.')
-param virtualMachineAdminUsername string = take(newGuid(), 20)
+param virtualMachineAdminUsername string?
@description('Optional. The password for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true.')
@secure()
-param virtualMachineAdminPassword string = newGuid()
+param virtualMachineAdminPassword string?
+
+@description('Optional. The size of the virtual machine. Defaults to Standard_D2s_v5.')
+param virtualMachineSize string = 'Standard_D2s_v5'
// These parameters are changed for testing - please reset as part of publication
@@ -138,7 +144,7 @@ param backendContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
param backendContainerImageName string = 'macaebackend'
@description('Optional. The Container Image Tag to deploy on the backend.')
-param backendContainerImageTag string = 'latest_v3'
+param backendContainerImageTag string = 'latest_v4'
@description('Optional. The Container Registry hostname where the docker images for the frontend are located.')
param frontendContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
@@ -147,7 +153,7 @@ param frontendContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
param frontendContainerImageName string = 'macaefrontend'
@description('Optional. The Container Image Tag to deploy on the frontend.')
-param frontendContainerImageTag string = 'latest_v3'
+param frontendContainerImageTag string = 'latest_v4'
@description('Optional. The Container Registry hostname where the docker images for the MCP are located.')
param MCPContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
@@ -156,7 +162,7 @@ param MCPContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
param MCPContainerImageName string = 'macaemcp'
@description('Optional. The Container Image Tag to deploy on the MCP.')
-param MCPContainerImageTag string = 'latest_v3'
+param MCPContainerImageTag string = 'latest_v4'
@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true
@@ -216,32 +222,36 @@ var replicaLocation = replicaRegionPairs[location]
// Resources //
// ============== //
-
var allTags = union(
{
'azd-env-name': solutionName
},
tags
)
+var existingTags = resourceGroup().tags ?? {}
@description('Tag, Created by user name')
-param createdBy string = contains(deployer(), 'userPrincipalName')? split(deployer().userPrincipalName, '@')[0]: deployer().objectId
-var deployerPrincipalType = contains(deployer(), 'userPrincipalName')? 'User' : 'ServicePrincipal'
+param createdBy string = contains(deployer(), 'userPrincipalName')
+ ? split(deployer().userPrincipalName, '@')[0]
+ : deployer().objectId
+var deployerPrincipalType = contains(deployer(), 'userPrincipalName') ? 'User' : 'ServicePrincipal'
resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = {
name: 'default'
properties: {
- tags: {
- ...resourceGroup().tags
- ...allTags
- TemplateName: 'MACAE'
- Type: enablePrivateNetworking ? 'WAF' : 'Non-WAF'
- CreatedBy: createdBy
- DeploymentName: deployment().name
- }
+ tags: union(
+ existingTags,
+ allTags,
+ {
+ TemplateName: 'MACAE'
+ Type: enablePrivateNetworking ? 'WAF' : 'Non-WAF'
+ CreatedBy: createdBy
+ DeploymentName: deployment().name
+ SolutionSuffix: solutionSuffix
+ }
+ )
}
}
-
#disable-next-line no-deployments-resources
resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
name: '46d3xbcp.ptn.sa-multiagentcustauteng.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
@@ -366,7 +376,6 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en
flowType: 'Bluefield'
// WAF aligned configuration for Monitoring
workspaceResourceId: enableMonitoring ? logAnalyticsWorkspaceResourceId : ''
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
}
}
@@ -412,7 +421,7 @@ module bastionHost 'br/public:avm/res/network/bastion-host:0.7.0' = if (enablePr
enableTelemetry: enableTelemetry
tags: tags
virtualNetworkResourceId: virtualNetwork!.?outputs.?resourceId
- availabilityZones:[]
+ availabilityZones: []
publicIPAddressObject: {
name: 'pip-bas${solutionSuffix}'
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
@@ -597,7 +606,6 @@ module proximityPlacementGroup 'br/public:avm/res/compute/proximity-placement-gr
var virtualMachineResourceName = 'vm-${solutionSuffix}'
var virtualMachineAvailabilityZone = 1
-var virtualMachineSize = 'Standard_D2s_v3'
module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (enablePrivateNetworking) {
name: take('avm.res.compute.virtual-machine.${virtualMachineResourceName}', 64)
params: {
@@ -608,8 +616,8 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (e
computerName: take(virtualMachineResourceName, 15)
osType: 'Windows'
vmSize: virtualMachineSize
- adminUsername: virtualMachineAdminUsername
- adminPassword: virtualMachineAdminPassword
+ adminUsername: virtualMachineAdminUsername ?? 'JumpboxAdminUser'
+ adminPassword: virtualMachineAdminPassword ?? 'JumpboxAdminP@ssw0rd1234!'
patchMode: 'AutomaticByPlatform'
bypassPlatformSafetyChecksOnUserSchedule: true
maintenanceConfigurationResourceId: maintenanceConfiguration!.outputs.resourceId
@@ -958,34 +966,48 @@ module aiFoundryAiServices 'br:mcr.microsoft.com/bicep/avm/res/cognitive-service
// WAF aligned configuration for Monitoring
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
- privateEndpoints: (enablePrivateNetworking)
- ? ([
- {
- name: 'pep-${aiFoundryAiServicesResourceName}'
- customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}'
- subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [
- {
- name: 'ai-services-dns-zone-cognitiveservices'
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId
- }
- {
- name: 'ai-services-dns-zone-openai'
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId
- }
- {
- name: 'ai-services-dns-zone-aiservices'
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId
- }
- ]
- }
- }
- ])
- : []
+ // Private endpoints are deployed separately via the aiFoundryPrivateEndpoint module below
+ privateEndpoints: []
+ }
+}
+
+module aiFoundryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.8.1' = if (enablePrivateNetworking && !useExistingAiFoundryAiProject) {
+ name: take('pep-${aiFoundryAiServicesResourceName}-deployment', 64)
+ params: {
+ name: 'pep-${aiFoundryAiServicesResourceName}'
+ customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}'
+ location: location
+ tags: tags
+ privateLinkServiceConnections: [
+ {
+ name: 'pep-${aiFoundryAiServicesResourceName}-connection'
+ properties: {
+ privateLinkServiceId: aiFoundryAiServices!.outputs.resourceId
+ groupIds: ['account']
+ }
+ }
+ ]
+ privateDnsZoneGroup: {
+ privateDnsZoneGroupConfigs: [
+ {
+ name: 'ai-services-dns-zone-cognitiveservices'
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId
+ }
+ {
+ name: 'ai-services-dns-zone-openai'
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId
+ }
+ {
+ name: 'ai-services-dns-zone-aiservices'
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId
+ }
+ ]
+ }
+ subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
}
}
+
resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/accounts/projects@2025-06-01' existing = if (useExistingAiFoundryAiProject) {
name: aiFoundryAiProjectResourceName
parent: existingAiFoundryAiServices
@@ -993,6 +1015,7 @@ resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/account
module aiFoundryAiServicesProject 'modules/ai-project.bicep' = if (!useExistingAiFoundryAiProject) {
name: take('module.ai-project.${aiFoundryAiProjectResourceName}', 64)
+ dependsOn: enablePrivateNetworking ? [ aiFoundryPrivateEndpoint ] : []
params: {
name: aiFoundryAiProjectResourceName
location: azureAiServiceLocation
@@ -1175,7 +1198,7 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
'https://${webSiteResourceName}.azurewebsites.net'
'http://${webSiteResourceName}.azurewebsites.net'
]
- allowedMethods:[
+ allowedMethods: [
'GET'
'POST'
'PUT'
@@ -1231,6 +1254,10 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
value: aiFoundryAiServicesModelDeployment.name
}
+ {
+ name: 'AZURE_OPENAI_RAI_DEPLOYMENT_NAME'
+ value: aiFoundryAiServices4_1ModelDeployment.name
+ }
{
name: 'AZURE_OPENAI_API_VERSION'
value: azureopenaiVersion
@@ -1259,10 +1286,10 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'FRONTEND_SITE_NAME'
value: 'https://${webSiteResourceName}.azurewebsites.net'
}
- {
- name: 'AZURE_AI_AGENT_ENDPOINT'
- value: aiFoundryAiProjectEndpoint
- }
+ // {
+ // name: 'AZURE_AI_AGENT_ENDPOINT'
+ // value: aiFoundryAiProjectEndpoint
+ // }
{
name: 'AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME'
value: aiFoundryAiServicesModelDeployment.name
@@ -1275,26 +1302,22 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'AZURE_AI_SEARCH_CONNECTION_NAME'
value: aiSearchConnectionName
}
- {
- name: 'AZURE_AI_SEARCH_INDEX_NAME'
- value: aiSearchIndexName
- }
{
name: 'AZURE_AI_SEARCH_ENDPOINT'
- value: searchService.outputs.endpoint
+ value: searchServiceUpdate.outputs.endpoint
}
{
name: 'AZURE_COGNITIVE_SERVICES'
- value: 'https://cognitiveservices.azure.com/.default'
+ value: 'https://cognitiveservices.azure.com/.default'
}
{
name: 'AZURE_BING_CONNECTION_NAME'
- value: 'binggrnd'
+ value: 'binggrnd'
}
{
name: 'BING_CONNECTION_NAME'
- value: 'binggrnd'
- }
+ value: 'binggrnd'
+ }
{
name: 'REASONING_MODEL_NAME'
value: aiFoundryAiServicesReasoningModelDeployment.name
@@ -1305,11 +1328,11 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
}
{
name: 'MCP_SERVER_NAME'
- value: 'MacaeMcpServer'
+ value: 'MacaeMcpServer'
}
{
name: 'MCP_SERVER_DESCRIPTION'
- value: 'MCP server with greeting, HR, and planning tools'
+ value: 'MCP server with greeting, HR, and planning tools'
}
{
name: 'AZURE_TENANT_ID'
@@ -1321,42 +1344,51 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
}
{
name: 'SUPPORTED_MODELS'
- value: '["o3","o4-mini","gpt-4.1","gpt-4.1-mini"]'
- }
- {
- name: 'AZURE_AI_SEARCH_API_KEY'
- secretRef: 'azure-ai-search-api-key'
- }
+ value: '["o3","o4-mini","gpt-4.1","gpt-4.1-mini"]'
+ }
{
name: 'AZURE_STORAGE_BLOB_URL'
value: avmStorageAccount.outputs.serviceEndpoints.blob
}
{
- name: 'AZURE_STORAGE_CONTAINER_NAME'
- value: storageContainerName
+ name: 'AZURE_AI_PROJECT_ENDPOINT'
+ value: aiFoundryAiProjectEndpoint
}
{
- name: 'AZURE_AI_MODEL_DEPLOYMENT_NAME'
- value: aiFoundryAiServicesModelDeployment.name
+ name: 'AZURE_AI_AGENT_ENDPOINT'
+ value: aiFoundryAiProjectEndpoint
+ }
+ {
+ name: 'AZURE_AI_AGENT_API_VERSION'
+ value: azureAiAgentAPIVersion
+ }
+ {
+ name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING'
+ value: '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
+ }
+ {
+ name: 'AZURE_BASIC_LOGGING_LEVEL'
+ value: 'INFO'
+ }
+ {
+ name: 'AZURE_PACKAGE_LOGGING_LEVEL'
+ value: 'WARNING'
+ }
+ {
+ name: 'AZURE_LOGGING_PACKAGES'
+ value: ''
}
]
-
- }
- ]
- secrets: [
- {
- name: 'azure-ai-search-api-key'
- keyVaultUrl: keyvault.outputs.secrets[0].uriWithVersion
- identity: userAssignedIdentity.outputs.resourceId
}
]
+ secrets: []
}
}
// ========== MCP Container App Service ========== //
// WAF best practices for container apps: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-container-apps
// PSRule for Container App: https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/#container-app
-var containerAppMcpResourceName = 'ca-mcp-${solutionSuffix}'
+var containerAppMcpResourceName = 'ca-mcp-${solutionSuffix}'
module containerAppMcp 'br/public:avm/res/app/container-app:0.18.1' = {
name: take('avm.res.app.container-app.${containerAppMcpResourceName}', 64)
params: {
@@ -1465,7 +1497,7 @@ module webServerFarm 'br/public:avm/res/web/serverfarm:0.5.0' = {
// WAF aligned configuration for Monitoring
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
// WAF aligned configuration for Scalability
- skuName: enableScalability || enableRedundancy ? 'P1v3' : 'B3'
+ skuName: enableScalability || enableRedundancy ? 'P1v4' : 'B3'
skuCapacity: enableScalability ? 3 : 1
// WAF aligned configuration for Redundancy
zoneRedundant: enableRedundancy ? true : false
@@ -1486,6 +1518,9 @@ module webSite 'modules/web-sites.bicep' = {
location: location
kind: 'app,linux,container'
serverFarmResourceId: webServerFarm.?outputs.resourceId
+ managedIdentities: {
+ systemAssigned: true
+ }
siteConfig: {
linuxFxVersion: 'DOCKER|${frontendContainerRegistryHostname}/${frontendContainerImageName}:${frontendContainerImageTag}'
minTlsVersion: '1.2'
@@ -1515,11 +1550,18 @@ module webSite 'modules/web-sites.bicep' = {
}
}
-
// ========== Storage Account ========== //
var storageAccountName = replace('st${solutionSuffix}', '-', '')
param storageContainerName string = 'sample-dataset'
+param storageContainerNameRetailCustomer string = 'retail-dataset-customer'
+param storageContainerNameRetailOrder string = 'retail-dataset-order'
+param storageContainerNameRFPSummary string = 'rfp-summary-dataset'
+param storageContainerNameRFPRisk string = 'rfp-risk-dataset'
+param storageContainerNameRFPCompliance string = 'rfp-compliance-dataset'
+param storageContainerNameContractSummary string = 'contract-summary-dataset'
+param storageContainerNameContractRisk string = 'contract-risk-dataset'
+param storageContainerNameContractCompliance string = 'contract-compliance-dataset'
module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
name: take('avm.res.storage.storage-account.${storageAccountName}', 64)
params: {
@@ -1548,13 +1590,13 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
// WAF aligned networking
networkAcls: {
bypass: 'AzureServices'
- defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow'
+ defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow'
}
allowBlobPublicAccess: false
- publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
+ publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
// Private endpoints for blob
- privateEndpoints: enablePrivateNetworking
+ privateEndpoints: enablePrivateNetworking
? [
{
name: 'pep-blob-${solutionSuffix}'
@@ -1578,7 +1620,35 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
containerDeleteRetentionPolicyEnabled: true
containers: [
{
- name: storageContainerName
+ name: storageContainerNameRetailCustomer
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRetailOrder
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRFPSummary
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRFPRisk
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRFPCompliance
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameContractSummary
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameContractRisk
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameContractCompliance
publicAccess: 'None'
}
]
@@ -1592,17 +1662,30 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
// ========== Search Service ========== //
var searchServiceName = 'srch-${solutionSuffix}'
-var aiSearchIndexName = 'sample-dataset-index'
-module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
- name: take('avm.res.search.search-service.${solutionSuffix}', 64)
+var aiSearchIndexNameForContractSummary = 'contract-summary-doc-index'
+var aiSearchIndexNameForContractRisk = 'contract-risk-doc-index'
+var aiSearchIndexNameForContractCompliance = 'contract-compliance-doc-index'
+var aiSearchIndexNameForRetailCustomer = 'macae-retail-customer-index'
+var aiSearchIndexNameForRetailOrder = 'macae-retail-order-index'
+var aiSearchIndexNameForRFPSummary = 'macae-rfp-summary-index'
+var aiSearchIndexNameForRFPRisk = 'macae-rfp-risk-index'
+var aiSearchIndexNameForRFPCompliance = 'macae-rfp-compliance-index'
+
+resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' = {
+ name: searchServiceName
+ location: location
+ sku: {
+ name: enableScalability ? 'standard' : 'basic'
+ }
+}
+
+// Separate module for Search Service to enable managed identity and update other properties, as this reduces deployment time
+module searchServiceUpdate 'br/public:avm/res/search/search-service:0.11.1' = {
+ name: take('avm.res.search.update.${solutionSuffix}', 64)
params: {
name: searchServiceName
- authOptions: {
- aadOrApiKey: {
- aadAuthFailureMode: 'http401WithBearerChallenge'
- }
- }
- disableLocalAuth: false
+ location: location
+ disableLocalAuth: true
hostingMode: 'default'
managedIdentities: {
systemAssigned: true
@@ -1644,7 +1727,7 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
//Removing the Private endpoints as we are facing the issue with connecting to search service while comminicating with agents
- privateEndpoints:[]
+ privateEndpoints: []
// privateEndpoints: enablePrivateNetworking
// ? [
// {
@@ -1663,6 +1746,9 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
// ]
// : []
}
+ dependsOn: [
+ searchService
+ ]
}
// ========== Search Service - AI Project Connection ========== //
@@ -1675,17 +1761,15 @@ module aiSearchFoundryConnection 'modules/aifp-connections.bicep' = {
aiFoundryProjectName: aiFoundryAiProjectName
aiFoundryName: aiFoundryAiServicesResourceName
aifSearchConnectionName: aiSearchConnectionName
- searchServiceResourceId: searchService.outputs.resourceId
- searchServiceLocation: searchService.outputs.location
- searchServiceName: searchService.outputs.name
- searchApiKey: searchService.outputs.primaryKey
+ searchServiceResourceId: searchService.id
+ searchServiceLocation: searchService.location
+ searchServiceName: searchService.name
}
dependsOn: [
aiFoundryAiServices
]
}
-
// ========== KeyVault ========== //
var keyVaultName = 'kv-${solutionSuffix}'
module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
@@ -1705,9 +1789,7 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
enableRbacAuthorization: true
enableSoftDelete: true
softDeleteRetentionInDays: 7
- diagnosticSettings: enableMonitoring
- ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }]
- : []
+ diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : []
// WAF aligned configuration for Private Networking
privateEndpoints: enablePrivateNetworking
? [
@@ -1715,7 +1797,9 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
name: 'pep-${keyVaultName}'
customNetworkInterfaceName: 'nic-${keyVaultName}'
privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [{ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.keyVault]!.outputs.resourceId }]
+ privateDnsZoneGroupConfigs: [
+ { privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.keyVault]!.outputs.resourceId }
+ ]
}
service: 'vault'
subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
@@ -1725,17 +1809,12 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
// WAF aligned configuration for Role-based Access Control
roleAssignments: [
{
- principalId: userAssignedIdentity.outputs.principalId
- principalType: 'ServicePrincipal'
- roleDefinitionIdOrName: 'Key Vault Administrator'
- }
- ]
- secrets: [
- {
- name: 'AzureAISearchAPIKey'
- value: searchService.outputs.primaryKey
+ principalId: userAssignedIdentity.outputs.principalId
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Key Vault Administrator'
}
]
+ secrets: []
enableTelemetry: enableTelemetry
}
}
@@ -1752,10 +1831,8 @@ output webSiteDefaultHostname string = webSite.outputs.defaultHostname
output AZURE_STORAGE_BLOB_URL string = avmStorageAccount.outputs.serviceEndpoints.blob
output AZURE_STORAGE_ACCOUNT_NAME string = storageAccountName
-output AZURE_STORAGE_CONTAINER_NAME string = storageContainerName
-output AZURE_AI_SEARCH_ENDPOINT string = searchService.outputs.endpoint
-output AZURE_AI_SEARCH_NAME string = searchService.outputs.name
-output AZURE_AI_SEARCH_INDEX_NAME string = aiSearchIndexName
+output AZURE_AI_SEARCH_ENDPOINT string = searchServiceUpdate.outputs.endpoint
+output AZURE_AI_SEARCH_NAME string = searchService.name
output COSMOSDB_ENDPOINT string = 'https://${cosmosDbResourceName}.documents.azure.com:443/'
output COSMOSDB_DATABASE string = cosmosDbDatabaseName
@@ -1763,27 +1840,51 @@ output COSMOSDB_CONTAINER string = cosmosDbDatabaseMemoryContainerName
output AZURE_OPENAI_ENDPOINT string = 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
output AZURE_OPENAI_MODEL_NAME string = aiFoundryAiServicesModelDeployment.name
output AZURE_OPENAI_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
+output AZURE_OPENAI_RAI_DEPLOYMENT_NAME string = aiFoundryAiServices4_1ModelDeployment.name
output AZURE_OPENAI_API_VERSION string = azureopenaiVersion
// output APPLICATIONINSIGHTS_INSTRUMENTATION_KEY string = applicationInsights.outputs.instrumentationKey
// output AZURE_AI_PROJECT_ENDPOINT string = aiFoundryAiServices.outputs.aiProjectInfo.apiEndpoint
output AZURE_AI_SUBSCRIPTION_ID string = subscription().subscriptionId
output AZURE_AI_RESOURCE_GROUP string = resourceGroup().name
output AZURE_AI_PROJECT_NAME string = aiFoundryAiProjectName
-output AZURE_AI_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
// output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.outputs.connectionString
output AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
-output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
+// output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
output APP_ENV string = 'Prod'
-output AI_FOUNDRY_RESOURCE_ID string = !useExistingAiFoundryAiProject ? aiFoundryAiServices.outputs.resourceId : existingAiFoundryAiProjectResourceId
+output AI_FOUNDRY_RESOURCE_ID string = !useExistingAiFoundryAiProject
+ ? aiFoundryAiServices.outputs.resourceId
+ : existingAiFoundryAiProjectResourceId
output COSMOSDB_ACCOUNT_NAME string = cosmosDbResourceName
-output AZURE_SEARCH_ENDPOINT string =searchService.outputs.endpoint
-output AZURE_CLIENT_ID string = userAssignedIdentity!.outputs.clientId
+output AZURE_SEARCH_ENDPOINT string = searchServiceUpdate.outputs.endpoint
+output AZURE_CLIENT_ID string = userAssignedIdentity!.outputs.clientId
output AZURE_TENANT_ID string = tenant().tenantId
-output AZURE_AI_SEARCH_CONNECTION_NAME string = aiSearchConnectionName
+output AZURE_AI_SEARCH_CONNECTION_NAME string = aiSearchConnectionName
output AZURE_COGNITIVE_SERVICES string = 'https://cognitiveservices.azure.com/.default'
output REASONING_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
output MCP_SERVER_NAME string = 'MacaeMcpServer'
output MCP_SERVER_DESCRIPTION string = 'MCP server with greeting, HR, and planning tools'
output SUPPORTED_MODELS string = '["o3","o4-mini","gpt-4.1","gpt-4.1-mini"]'
-output AZURE_AI_SEARCH_API_KEY string = ''
output BACKEND_URL string = 'https://${containerApp.outputs.fqdn}'
+output AZURE_AI_PROJECT_ENDPOINT string = aiFoundryAiProjectEndpoint
+output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
+output AZURE_AI_AGENT_API_VERSION string = azureAiAgentAPIVersion
+output AZURE_AI_AGENT_PROJECT_CONNECTION_STRING string = '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
+
+
+output AZURE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER string = storageContainerNameRetailCustomer
+output AZURE_STORAGE_CONTAINER_NAME_RETAIL_ORDER string = storageContainerNameRetailOrder
+output AZURE_STORAGE_CONTAINER_NAME_RFP_SUMMARY string = storageContainerNameRFPSummary
+output AZURE_STORAGE_CONTAINER_NAME_RFP_RISK string = storageContainerNameRFPRisk
+output AZURE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE string = storageContainerNameRFPCompliance
+output AZURE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY string = storageContainerNameContractSummary
+output AZURE_STORAGE_CONTAINER_NAME_CONTRACT_RISK string = storageContainerNameContractRisk
+output AZURE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE string = storageContainerNameContractCompliance
+output AZURE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER string = aiSearchIndexNameForRetailCustomer
+output AZURE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER string = aiSearchIndexNameForRetailOrder
+output AZURE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY string = aiSearchIndexNameForRFPSummary
+output AZURE_AI_SEARCH_INDEX_NAME_RFP_RISK string = aiSearchIndexNameForRFPRisk
+output AZURE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE string = aiSearchIndexNameForRFPCompliance
+output AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY string = aiSearchIndexNameForContractSummary
+output AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK string = aiSearchIndexNameForContractRisk
+output AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE string = aiSearchIndexNameForContractCompliance
+
diff --git a/infra/main.json b/infra/main.json
new file mode 100644
index 000000000..9c32501af
--- /dev/null
+++ b/infra/main.json
@@ -0,0 +1,48902 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.41.2.15936",
+ "templateHash": "797205848378228584"
+ },
+ "name": "Multi-Agent Custom Automation Engine",
+ "description": "This module contains the resources required to deploy the [Multi-Agent Custom Automation Engine solution accelerator](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) for both Sandbox environments and WAF aligned environments.\n\n> **Note:** This module is not intended for broad, generic use, as it was designed by the Commercial Solution Areas CTO team, as a Microsoft Solution Accelerator. Feature requests and bug fix requests are welcome if they support the needs of this organization but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. This module will likely be updated to leverage AVM resource modules in the future. This may result in breaking changes in upcoming versions when these features are implemented.\n"
+ },
+ "parameters": {
+ "solutionName": {
+ "type": "string",
+ "defaultValue": "macae",
+ "minLength": 3,
+ "maxLength": 16,
+ "metadata": {
+ "description": "Optional. A unique application/solution name for all resources in this deployment. This should be 3-16 characters long."
+ }
+ },
+ "solutionUniqueText": {
+ "type": "string",
+ "defaultValue": "[take(uniqueString(subscription().id, resourceGroup().name, parameters('solutionName')), 5)]",
+ "maxLength": 5,
+ "metadata": {
+ "description": "Optional. A unique text value for the solution. This is used to ensure resource names are unique for global resources. Defaults to a 5-character substring of the unique string generated from the subscription ID, resource group name, and solution name."
+ }
+ },
+ "location": {
+ "type": "string",
+ "allowedValues": [
+ "australiaeast",
+ "centralus",
+ "eastasia",
+ "eastus2",
+ "japaneast",
+ "northeurope",
+ "southeastasia",
+ "uksouth"
+ ],
+ "metadata": {
+ "azd": {
+ "type": "location"
+ },
+ "description": "Required. Azure region for all services. Regions are restricted to guarantee compatibility with paired regions and replica locations for data redundancy and failover scenarios based on articles [Azure regions list](https://learn.microsoft.com/azure/reliability/regions-list) and [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions)."
+ }
+ },
+ "azureAiServiceLocation": {
+ "type": "string",
+ "allowedValues": [
+ "australiaeast",
+ "eastus2",
+ "francecentral",
+ "japaneast",
+ "norwayeast",
+ "swedencentral",
+ "uksouth",
+ "westus"
+ ],
+ "metadata": {
+ "azd": {
+ "type": "location",
+ "usageName": [
+ "OpenAI.GlobalStandard.gpt4.1, 150",
+ "OpenAI.GlobalStandard.o4-mini, 50",
+ "OpenAI.GlobalStandard.gpt4.1-mini, 50"
+ ]
+ },
+ "description": "Required. Location for all AI service resources. This should be one of the supported Azure AI Service locations."
+ }
+ },
+ "gptModelName": {
+ "type": "string",
+ "defaultValue": "gpt-4.1-mini",
+ "minLength": 1,
+ "metadata": {
+ "description": "Optional. Name of the GPT model to deploy:"
+ }
+ },
+ "gptModelVersion": {
+ "type": "string",
+ "defaultValue": "2025-04-14",
+ "metadata": {
+ "description": "Optional. Version of the GPT model to deploy. Defaults to 2025-04-14."
+ }
+ },
+ "gpt4_1ModelName": {
+ "type": "string",
+ "defaultValue": "gpt-4.1",
+ "minLength": 1,
+ "metadata": {
+ "description": "Optional. Name of the GPT model to deploy:"
+ }
+ },
+ "gpt4_1ModelVersion": {
+ "type": "string",
+ "defaultValue": "2025-04-14",
+ "metadata": {
+ "description": "Optional. Version of the GPT model to deploy. Defaults to 2025-04-14."
+ }
+ },
+ "gptReasoningModelName": {
+ "type": "string",
+ "defaultValue": "o4-mini",
+ "minLength": 1,
+ "metadata": {
+ "description": "Optional. Name of the GPT Reasoning model to deploy:"
+ }
+ },
+ "gptReasoningModelVersion": {
+ "type": "string",
+ "defaultValue": "2025-04-16",
+ "metadata": {
+ "description": "Optional. Version of the GPT Reasoning model to deploy. Defaults to 2025-04-16."
+ }
+ },
+ "azureopenaiVersion": {
+ "type": "string",
+ "defaultValue": "2024-12-01-preview",
+ "metadata": {
+ "description": "Optional. Version of the Azure OpenAI service to deploy. Defaults to 2024-12-01-preview."
+ }
+ },
+ "azureAiAgentAPIVersion": {
+ "type": "string",
+ "defaultValue": "2025-01-01-preview",
+ "metadata": {
+ "description": "Optional. Version of the Azure AI Agent API version. Defaults to 2025-01-01-preview."
+ }
+ },
+ "gpt4_1ModelDeploymentType": {
+ "type": "string",
+ "defaultValue": "GlobalStandard",
+ "allowedValues": [
+ "Standard",
+ "GlobalStandard"
+ ],
+ "minLength": 1,
+ "metadata": {
+ "description": "Optional. GPT model deployment type. Defaults to GlobalStandard."
+ }
+ },
+ "gptModelDeploymentType": {
+ "type": "string",
+ "defaultValue": "GlobalStandard",
+ "allowedValues": [
+ "Standard",
+ "GlobalStandard"
+ ],
+ "minLength": 1,
+ "metadata": {
+ "description": "Optional. GPT model deployment type. Defaults to GlobalStandard."
+ }
+ },
+ "gptReasoningModelDeploymentType": {
+ "type": "string",
+ "defaultValue": "GlobalStandard",
+ "allowedValues": [
+ "Standard",
+ "GlobalStandard"
+ ],
+ "minLength": 1,
+ "metadata": {
+ "description": "Optional. GPT model deployment type. Defaults to GlobalStandard."
+ }
+ },
+ "gptModelCapacity": {
+ "type": "int",
+ "defaultValue": 50,
+ "metadata": {
+ "description": "Optional. AI model deployment token capacity. Defaults to 50 for optimal performance."
+ }
+ },
+ "gpt4_1ModelCapacity": {
+ "type": "int",
+ "defaultValue": 150,
+ "metadata": {
+ "description": "Optional. AI model deployment token capacity. Defaults to 150 for optimal performance."
+ }
+ },
+ "gptReasoningModelCapacity": {
+ "type": "int",
+ "defaultValue": 50,
+ "metadata": {
+ "description": "Optional. AI model deployment token capacity. Defaults to 50 for optimal performance."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Resources/resourceGroups@2025-04-01#properties/tags"
+ },
+ "description": "Optional. The tags to apply to all deployed Azure resources."
+ },
+ "defaultValue": {}
+ },
+ "enableMonitoring": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable monitoring applicable resources, aligned with the Well Architected Framework recommendations. This setting enables Application Insights and Log Analytics and configures all the resources applicable resources to send logs. Defaults to false."
+ }
+ },
+ "enableScalability": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable scalability for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false."
+ }
+ },
+ "enableRedundancy": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable redundancy for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false."
+ }
+ },
+ "enablePrivateNetworking": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable private networking for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false."
+ }
+ },
+ "virtualMachineAdminUsername": {
+ "type": "securestring",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The user name for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true."
+ }
+ },
+ "virtualMachineAdminPassword": {
+ "type": "securestring",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The password for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true."
+ }
+ },
+ "virtualMachineSize": {
+ "type": "string",
+ "defaultValue": "Standard_D2s_v5",
+ "metadata": {
+ "description": "Optional. The size of the virtual machine. Defaults to Standard_D2s_v5."
+ }
+ },
+ "backendContainerRegistryHostname": {
+ "type": "string",
+ "defaultValue": "biabcontainerreg.azurecr.io",
+ "metadata": {
+ "description": "Optional. The Container Registry hostname where the docker images for the backend are located."
+ }
+ },
+ "backendContainerImageName": {
+ "type": "string",
+ "defaultValue": "macaebackend",
+ "metadata": {
+ "description": "Optional. The Container Image Name to deploy on the backend."
+ }
+ },
+ "backendContainerImageTag": {
+ "type": "string",
+ "defaultValue": "latest_v4",
+ "metadata": {
+ "description": "Optional. The Container Image Tag to deploy on the backend."
+ }
+ },
+ "frontendContainerRegistryHostname": {
+ "type": "string",
+ "defaultValue": "biabcontainerreg.azurecr.io",
+ "metadata": {
+ "description": "Optional. The Container Registry hostname where the docker images for the frontend are located."
+ }
+ },
+ "frontendContainerImageName": {
+ "type": "string",
+ "defaultValue": "macaefrontend",
+ "metadata": {
+ "description": "Optional. The Container Image Name to deploy on the frontend."
+ }
+ },
+ "frontendContainerImageTag": {
+ "type": "string",
+ "defaultValue": "latest_v4",
+ "metadata": {
+ "description": "Optional. The Container Image Tag to deploy on the frontend."
+ }
+ },
+ "MCPContainerRegistryHostname": {
+ "type": "string",
+ "defaultValue": "biabcontainerreg.azurecr.io",
+ "metadata": {
+ "description": "Optional. The Container Registry hostname where the docker images for the MCP are located."
+ }
+ },
+ "MCPContainerImageName": {
+ "type": "string",
+ "defaultValue": "macaemcp",
+ "metadata": {
+ "description": "Optional. The Container Image Name to deploy on the MCP."
+ }
+ },
+ "MCPContainerImageTag": {
+ "type": "string",
+ "defaultValue": "latest_v4",
+ "metadata": {
+ "description": "Optional. The Container Image Tag to deploy on the MCP."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "existingLogAnalyticsWorkspaceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Resource ID of an existing Log Analytics Workspace."
+ }
+ },
+ "existingAiFoundryAiProjectResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Resource ID of an existing Ai Foundry AI Services resource."
+ }
+ },
+ "createdBy": {
+ "type": "string",
+ "defaultValue": "[if(contains(deployer(), 'userPrincipalName'), split(deployer().userPrincipalName, '@')[0], deployer().objectId)]",
+ "metadata": {
+ "description": "Tag, Created by user name"
+ }
+ },
+ "storageContainerName": {
+ "type": "string",
+ "defaultValue": "sample-dataset"
+ },
+ "storageContainerNameRetailCustomer": {
+ "type": "string",
+ "defaultValue": "retail-dataset-customer"
+ },
+ "storageContainerNameRetailOrder": {
+ "type": "string",
+ "defaultValue": "retail-dataset-order"
+ },
+ "storageContainerNameRFPSummary": {
+ "type": "string",
+ "defaultValue": "rfp-summary-dataset"
+ },
+ "storageContainerNameRFPRisk": {
+ "type": "string",
+ "defaultValue": "rfp-risk-dataset"
+ },
+ "storageContainerNameRFPCompliance": {
+ "type": "string",
+ "defaultValue": "rfp-compliance-dataset"
+ },
+ "storageContainerNameContractSummary": {
+ "type": "string",
+ "defaultValue": "contract-summary-dataset"
+ },
+ "storageContainerNameContractRisk": {
+ "type": "string",
+ "defaultValue": "contract-risk-dataset"
+ },
+ "storageContainerNameContractCompliance": {
+ "type": "string",
+ "defaultValue": "contract-compliance-dataset"
+ }
+ },
+ "variables": {
+ "deployerInfo": "[deployer()]",
+ "deployingUserPrincipalId": "[variables('deployerInfo').objectId]",
+ "solutionSuffix": "[toLower(trim(replace(replace(replace(replace(replace(replace(format('{0}{1}', parameters('solutionName'), parameters('solutionUniqueText')), '-', ''), '_', ''), '.', ''), '/', ''), ' ', ''), '*', '')))]",
+ "cosmosDbZoneRedundantHaRegionPairs": {
+ "australiaeast": "uksouth",
+ "centralus": "eastus2",
+ "eastasia": "southeastasia",
+ "eastus": "centralus",
+ "eastus2": "centralus",
+ "japaneast": "australiaeast",
+ "northeurope": "westeurope",
+ "southeastasia": "eastasia",
+ "uksouth": "westeurope",
+ "westeurope": "northeurope"
+ },
+ "cosmosDbHaLocation": "[variables('cosmosDbZoneRedundantHaRegionPairs')[parameters('location')]]",
+ "replicaRegionPairs": {
+ "australiaeast": "australiasoutheast",
+ "centralus": "westus",
+ "eastasia": "japaneast",
+ "eastus": "centralus",
+ "eastus2": "centralus",
+ "japaneast": "eastasia",
+ "northeurope": "westeurope",
+ "southeastasia": "eastasia",
+ "uksouth": "westeurope",
+ "westeurope": "northeurope"
+ },
+ "replicaLocation": "[variables('replicaRegionPairs')[parameters('location')]]",
+ "allTags": "[union(createObject('azd-env-name', parameters('solutionName')), parameters('tags'))]",
+ "existingTags": "[coalesce(resourceGroup().tags, createObject())]",
+ "deployerPrincipalType": "[if(contains(deployer(), 'userPrincipalName'), 'User', 'ServicePrincipal')]",
+ "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], '')]",
+ "logAnalyticsWorkspaceResourceName": "[format('log-{0}', variables('solutionSuffix'))]",
+ "applicationInsightsResourceName": "[format('appi-{0}', variables('solutionSuffix'))]",
+ "userAssignedIdentityResourceName": "[format('id-{0}', variables('solutionSuffix'))]",
+ "virtualNetworkResourceName": "[format('vnet-{0}', variables('solutionSuffix'))]",
+ "bastionResourceName": "[format('bas-{0}', variables('solutionSuffix'))]",
+ "maintenanceConfigurationResourceName": "[format('mc-{0}', variables('solutionSuffix'))]",
+ "dataCollectionRulesResourceName": "[format('dcr-{0}', variables('solutionSuffix'))]",
+ "proximityPlacementGroupResourceName": "[format('ppg-{0}', variables('solutionSuffix'))]",
+ "virtualMachineResourceName": "[format('vm-{0}', variables('solutionSuffix'))]",
+ "virtualMachineAvailabilityZone": 1,
+ "keyVaultPrivateDNSZone": "[format('privatelink.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'vaultcore.usgovcloudapi.net', 'vaultcore.azure.net'))]",
+ "privateDnsZones": [
+ "privatelink.cognitiveservices.azure.com",
+ "privatelink.openai.azure.com",
+ "privatelink.services.ai.azure.com",
+ "privatelink.documents.azure.com",
+ "privatelink.blob.core.windows.net",
+ "privatelink.search.windows.net",
+ "[variables('keyVaultPrivateDNSZone')]"
+ ],
+ "dnsZoneIndex": {
+ "cognitiveServices": 0,
+ "openAI": 1,
+ "aiServices": 2,
+ "cosmosDb": 3,
+ "blob": 4,
+ "search": 5,
+ "keyVault": 6
+ },
+ "aiRelatedDnsZoneIndices": [
+ "[variables('dnsZoneIndex').cognitiveServices]",
+ "[variables('dnsZoneIndex').openAI]",
+ "[variables('dnsZoneIndex').aiServices]"
+ ],
+ "useExistingAiFoundryAiProject": "[not(empty(parameters('existingAiFoundryAiProjectResourceId')))]",
+ "aiFoundryAiServicesResourceGroupName": "[if(variables('useExistingAiFoundryAiProject'), split(parameters('existingAiFoundryAiProjectResourceId'), '/')[4], resourceGroup().name)]",
+ "aiFoundryAiServicesSubscriptionId": "[if(variables('useExistingAiFoundryAiProject'), split(parameters('existingAiFoundryAiProjectResourceId'), '/')[2], subscription().subscriptionId)]",
+ "aiFoundryAiServicesResourceName": "[if(variables('useExistingAiFoundryAiProject'), split(parameters('existingAiFoundryAiProjectResourceId'), '/')[8], format('aif-{0}', variables('solutionSuffix')))]",
+ "aiFoundryAiProjectResourceName": "[if(variables('useExistingAiFoundryAiProject'), split(parameters('existingAiFoundryAiProjectResourceId'), '/')[10], format('proj-{0}', variables('solutionSuffix')))]",
+ "aiFoundryAiServicesModelDeployment": {
+ "format": "OpenAI",
+ "name": "[parameters('gptModelName')]",
+ "version": "[parameters('gptModelVersion')]",
+ "sku": {
+ "name": "[parameters('gptModelDeploymentType')]",
+ "capacity": "[parameters('gptModelCapacity')]"
+ },
+ "raiPolicyName": "Microsoft.Default"
+ },
+ "aiFoundryAiServices4_1ModelDeployment": {
+ "format": "OpenAI",
+ "name": "[parameters('gpt4_1ModelName')]",
+ "version": "[parameters('gpt4_1ModelVersion')]",
+ "sku": {
+ "name": "[parameters('gpt4_1ModelDeploymentType')]",
+ "capacity": "[parameters('gpt4_1ModelCapacity')]"
+ },
+ "raiPolicyName": "Microsoft.Default"
+ },
+ "aiFoundryAiServicesReasoningModelDeployment": {
+ "format": "OpenAI",
+ "name": "[parameters('gptReasoningModelName')]",
+ "version": "[parameters('gptReasoningModelVersion')]",
+ "sku": {
+ "name": "[parameters('gptReasoningModelDeploymentType')]",
+ "capacity": "[parameters('gptReasoningModelCapacity')]"
+ },
+ "raiPolicyName": "Microsoft.Default"
+ },
+ "aiFoundryAiProjectDescription": "AI Foundry Project",
+ "cosmosDbResourceName": "[format('cosmos-{0}', variables('solutionSuffix'))]",
+ "cosmosDbDatabaseName": "macae",
+ "cosmosDbDatabaseMemoryContainerName": "memory",
+ "containerAppEnvironmentResourceName": "[format('cae-{0}', variables('solutionSuffix'))]",
+ "containerAppResourceName": "[format('ca-{0}', variables('solutionSuffix'))]",
+ "containerAppMcpResourceName": "[format('ca-mcp-{0}', variables('solutionSuffix'))]",
+ "webServerFarmResourceName": "[format('asp-{0}', variables('solutionSuffix'))]",
+ "webSiteResourceName": "[format('app-{0}', variables('solutionSuffix'))]",
+ "storageAccountName": "[replace(format('st{0}', variables('solutionSuffix')), '-', '')]",
+ "searchServiceName": "[format('srch-{0}', variables('solutionSuffix'))]",
+ "aiSearchIndexNameForContractSummary": "contract-summary-doc-index",
+ "aiSearchIndexNameForContractRisk": "contract-risk-doc-index",
+ "aiSearchIndexNameForContractCompliance": "contract-compliance-doc-index",
+ "aiSearchIndexNameForRetailCustomer": "macae-retail-customer-index",
+ "aiSearchIndexNameForRetailOrder": "macae-retail-order-index",
+ "aiSearchIndexNameForRFPSummary": "macae-rfp-summary-index",
+ "aiSearchIndexNameForRFPRisk": "macae-rfp-risk-index",
+ "aiSearchIndexNameForRFPCompliance": "macae-rfp-compliance-index",
+ "aiSearchConnectionName": "[format('aifp-srch-connection-{0}', variables('solutionSuffix'))]",
+ "keyVaultName": "[format('kv-{0}', variables('solutionSuffix'))]"
+ },
+ "resources": {
+ "resourceGroupTags": {
+ "type": "Microsoft.Resources/tags",
+ "apiVersion": "2021-04-01",
+ "name": "default",
+ "properties": {
+ "tags": "[union(variables('existingTags'), variables('allTags'), createObject('TemplateName', 'MACAE', 'Type', if(parameters('enablePrivateNetworking'), 'WAF', 'Non-WAF'), 'CreatedBy', parameters('createdBy'), 'DeploymentName', deployment().name, 'SolutionSuffix', variables('solutionSuffix')))]"
+ }
+ },
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.ptn.sa-multiagentcustauteng.{0}.{1}', replace('-..--..-', '.', '-'), 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"
+ }
+ }
+ }
+ }
+ },
+ "existingLogAnalyticsWorkspace": {
+ "condition": "[variables('useExistingLogAnalytics')]",
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2020-08-01",
+ "subscriptionId": "[variables('existingLawSubscription')]",
+ "resourceGroup": "[variables('existingLawResourceGroup')]",
+ "name": "[variables('existingLawName')]"
+ },
+ "existingAiFoundryAiServices": {
+ "condition": "[variables('useExistingAiFoundryAiProject')]",
+ "existing": true,
+ "type": "Microsoft.CognitiveServices/accounts",
+ "apiVersion": "2025-06-01",
+ "subscriptionId": "[variables('aiFoundryAiServicesSubscriptionId')]",
+ "resourceGroup": "[variables('aiFoundryAiServicesResourceGroupName')]",
+ "name": "[variables('aiFoundryAiServicesResourceName')]"
+ },
+ "existingAiFoundryAiServicesProject": {
+ "condition": "[variables('useExistingAiFoundryAiProject')]",
+ "existing": true,
+ "type": "Microsoft.CognitiveServices/accounts/projects",
+ "apiVersion": "2025-06-01",
+ "subscriptionId": "[variables('aiFoundryAiServicesSubscriptionId')]",
+ "resourceGroup": "[variables('aiFoundryAiServicesResourceGroupName')]",
+ "name": "[format('{0}/{1}', variables('aiFoundryAiServicesResourceName'), variables('aiFoundryAiProjectResourceName'))]"
+ },
+ "searchService": {
+ "type": "Microsoft.Search/searchServices",
+ "apiVersion": "2024-06-01-preview",
+ "name": "[variables('searchServiceName')]",
+ "location": "[parameters('location')]",
+ "sku": {
+ "name": "[if(parameters('enableScalability'), 'standard', 'basic')]"
+ }
+ },
+ "logAnalyticsWorkspace": {
+ "condition": "[and(parameters('enableMonitoring'), not(variables('useExistingLogAnalytics')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.operational-insights.workspace.{0}', variables('logAnalyticsWorkspaceResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('logAnalyticsWorkspaceResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "skuName": {
+ "value": "PerGB2018"
+ },
+ "dataRetention": {
+ "value": 365
+ },
+ "features": {
+ "value": {
+ "enableLogAccessUsingOnlyResourcePermissions": true
+ }
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "useThisWorkspace": true
+ }
+ ]
+ },
+ "dailyQuotaGb": "[if(parameters('enableRedundancy'), createObject('value', 150), createObject('value', null()))]",
+ "replication": "[if(parameters('enableRedundancy'), createObject('value', createObject('enabled', true(), 'location', variables('replicaLocation'))), createObject('value', null()))]",
+ "publicNetworkAccessForIngestion": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]",
+ "publicNetworkAccessForQuery": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]",
+ "dataSources": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('tags', parameters('tags'), 'eventLogName', 'Application', 'eventTypes', createArray(createObject('eventType', 'Error'), createObject('eventType', 'Warning'), createObject('eventType', 'Information')), 'kind', 'WindowsEvent', 'name', 'applicationEvent'), createObject('counterName', '% Processor Time', 'instanceName', '*', 'intervalSeconds', 60, 'kind', 'WindowsPerformanceCounter', 'name', 'windowsPerfCounter1', 'objectName', 'Processor'), createObject('kind', 'IISLogs', 'name', 'sampleIISLog1', 'state', 'OnPremiseEnabled'))), createObject('value', 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.36.1.42791",
+ "templateHash": "1749032521457140145"
+ },
+ "name": "Log Analytics Workspaces",
+ "description": "This module deploys a Log Analytics Workspace."
+ },
+ "definitions": {
+ "diagnosticSettingType": {
+ "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."
+ }
+ },
+ "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."
+ }
+ },
+ "useThisWorkspace": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. If set to `true`, the `workspaceResourceId` property is ignored."
+ }
+ },
+ "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."
+ }
+ }
+ }
+ },
+ "gallerySolutionType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the solution.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, the name should be in the pattern: `SolutionType[WorkspaceName]`, for example `MySolution[contoso-Logs]`.\nThe solution type is case-sensitive."
+ }
+ },
+ "plan": {
+ "$ref": "#/definitions/solutionPlanType",
+ "metadata": {
+ "description": "Required. Plan for solution object supported by the OperationsManagement resource provider."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the gallery solutions to be created in the log analytics workspace."
+ }
+ },
+ "storageInsightsConfigType": {
+ "type": "object",
+ "properties": {
+ "storageAccountResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the storage account to be linked."
+ }
+ },
+ "containers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The names of the blob containers that the workspace should read."
+ }
+ },
+ "tables": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of tables to be read by the workspace."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the storage insights configuration."
+ }
+ },
+ "linkedServiceType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the linked service."
+ }
+ },
+ "resourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource id of the resource that will be linked to the workspace. This should be used for linking resources which require read access."
+ }
+ },
+ "writeAccessResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource id of the resource that will be linked to the workspace. This should be used for linking resources which require write access."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the linked service."
+ }
+ },
+ "linkedStorageAccountType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the link."
+ }
+ },
+ "storageAccountIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minLength": 1,
+ "metadata": {
+ "description": "Required. Linked storage accounts resources Ids."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the linked storage account."
+ }
+ },
+ "savedSearchType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the saved search."
+ }
+ },
+ "etag": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The ETag of the saved search. To override an existing saved search, use \"*\" or specify the current Etag."
+ }
+ },
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The category of the saved search. This helps the user to find a saved search faster."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Display name for the search."
+ }
+ },
+ "functionAlias": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The function alias if query serves as a function."
+ }
+ },
+ "functionParameters": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The optional function parameters if query serves as a function. Value should be in the following format: 'param-name1:type1 = default_value1, param-name2:type2 = default_value2'. For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions."
+ }
+ },
+ "query": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The query expression for the saved search."
+ }
+ },
+ "tags": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags attached to the saved search."
+ }
+ },
+ "version": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The version number of the query language. The current version is 2 and is the default."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the saved search."
+ }
+ },
+ "dataExportType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the data export."
+ }
+ },
+ "destination": {
+ "$ref": "#/definitions/destinationType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The destination of the data export."
+ }
+ },
+ "enable": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the data export."
+ }
+ },
+ "tableNames": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "metadata": {
+ "description": "Required. The list of table names to export."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the data export."
+ }
+ },
+ "dataSourceType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the data source."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The kind of data source."
+ }
+ },
+ "linkedResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource id of the resource that will be linked to the workspace."
+ }
+ },
+ "eventLogName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the event log to configure when kind is WindowsEvent."
+ }
+ },
+ "eventTypes": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The event types to configure when kind is WindowsEvent."
+ }
+ },
+ "objectName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject."
+ }
+ },
+ "instanceName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject."
+ }
+ },
+ "intervalSeconds": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject."
+ }
+ },
+ "performanceCounters": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of counters to configure when the kind is LinuxPerformanceObject."
+ }
+ },
+ "counterName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Counter name to configure when kind is WindowsPerformanceCounter."
+ }
+ },
+ "state": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection."
+ }
+ },
+ "syslogName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. System log to configure when kind is LinuxSyslog."
+ }
+ },
+ "syslogSeverities": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Severities to configure when kind is LinuxSyslog."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.OperationalInsights/workspaces/dataSources@2025-02-01#properties/tags"
+ },
+ "description": "Optional. Tags to configure in the resource."
+ },
+ "nullable": true
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the data source."
+ }
+ },
+ "tableType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the table."
+ }
+ },
+ "plan": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The plan for the table."
+ }
+ },
+ "restoredLogs": {
+ "$ref": "#/definitions/restoredLogsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The restored logs for the table."
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/schemaType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The schema for the table."
+ }
+ },
+ "searchResults": {
+ "$ref": "#/definitions/searchResultsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The search results for the table."
+ }
+ },
+ "retentionInDays": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The retention in days for the table."
+ }
+ },
+ "totalRetentionInDays": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The total retention in days for the table."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The role assignments for the table."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Properties of the custom table."
+ }
+ },
+ "workspaceFeaturesType": {
+ "type": "object",
+ "properties": {
+ "disableLocalAuth": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Disable Non-EntraID based Auth. Default is true."
+ }
+ },
+ "enableDataExport": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Flag that indicate if data should be exported."
+ }
+ },
+ "enableLogAccessUsingOnlyResourcePermissions": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable log access using only resource permissions. Default is false."
+ }
+ },
+ "immediatePurgeDataOn30Days": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Flag that describes if we want to remove the data after 30 days."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Features of the workspace."
+ }
+ },
+ "workspaceReplicationType": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies whether the replication is enabled or not. When true, workspace configuration and data is replicated to the specified location."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The location to which the workspace is replicated. Required if replication is enabled."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Replication properties of the workspace."
+ }
+ },
+ "_1.columnType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The column name."
+ }
+ },
+ "type": {
+ "type": "string",
+ "allowedValues": [
+ "boolean",
+ "dateTime",
+ "dynamic",
+ "guid",
+ "int",
+ "long",
+ "real",
+ "string"
+ ],
+ "metadata": {
+ "description": "Required. The column type."
+ }
+ },
+ "dataTypeHint": {
+ "type": "string",
+ "allowedValues": [
+ "armPath",
+ "guid",
+ "ip",
+ "uri"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The column data type logical hint."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The column description."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Column display name."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The parameters of the table column.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "table/main.bicep"
+ }
+ }
+ },
+ "destinationType": {
+ "type": "object",
+ "properties": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The destination resource ID."
+ }
+ },
+ "metaData": {
+ "type": "object",
+ "properties": {
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Allows to define an Event Hub name. Not applicable when destination is Storage Account."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The destination metadata."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The data export destination properties.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "data-export/main.bicep"
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "restoredLogsType": {
+ "type": "object",
+ "properties": {
+ "sourceTable": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The table to restore data from."
+ }
+ },
+ "startRestoreTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to start the restore from (UTC)."
+ }
+ },
+ "endRestoreTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to end the restore by (UTC)."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The parameters of the restore operation that initiated the table.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "table/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"
+ }
+ }
+ },
+ "schemaType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The table name."
+ }
+ },
+ "columns": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.columnType"
+ },
+ "metadata": {
+ "description": "Required. A list of table custom columns."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The table description."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The table display name."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The table schema.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "table/main.bicep"
+ }
+ }
+ },
+ "searchResultsType": {
+ "type": "object",
+ "properties": {
+ "query": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The search job query."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The search description."
+ }
+ },
+ "limit": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Limit the search job to return up to specified number of rows."
+ }
+ },
+ "startSearchTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to start the search from (UTC)."
+ }
+ },
+ "endSearchTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to end the search by (UTC)."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The parameters of the search job that initiated the table.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "table/main.bicep"
+ }
+ }
+ },
+ "solutionPlanType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the solution to be created.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, it can be anything.\nThe solution type is case-sensitive.\nIf not provided, the value of the `name` parameter will be used."
+ }
+ },
+ "product": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The product name of the deployed solution.\nFor Microsoft published gallery solution it should be `OMSGallery/{solutionType}`, for example `OMSGallery/AntiMalware`.\nFor a third party solution, it can be anything.\nThis is case sensitive."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`, which is the default value."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/operations-management/solution:0.3.1"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Log Analytics workspace."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "defaultValue": "PerGB2018",
+ "allowedValues": [
+ "CapacityReservation",
+ "Free",
+ "LACluster",
+ "PerGB2018",
+ "PerNode",
+ "Premium",
+ "Standalone",
+ "Standard"
+ ],
+ "metadata": {
+ "description": "Optional. The name of the SKU."
+ }
+ },
+ "skuCapacityReservationLevel": {
+ "type": "int",
+ "defaultValue": 100,
+ "minValue": 100,
+ "maxValue": 5000,
+ "metadata": {
+ "description": "Optional. The capacity reservation level in GB for this workspace, when CapacityReservation sku is selected. Must be in increments of 100 between 100 and 5000."
+ }
+ },
+ "storageInsightsConfigs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/storageInsightsConfigType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of storage accounts to be read by the workspace."
+ }
+ },
+ "linkedServices": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/linkedServiceType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of services to be linked."
+ }
+ },
+ "linkedStorageAccounts": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/linkedStorageAccountType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. List of Storage Accounts to be linked. Required if 'forceCmkForQuery' is set to 'true' and 'savedSearches' is not empty."
+ }
+ },
+ "savedSearches": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/savedSearchType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Kusto Query Language searches to save."
+ }
+ },
+ "dataExports": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/dataExportType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. LAW data export instances to be deployed."
+ }
+ },
+ "dataSources": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/dataSourceType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. LAW data sources to configure."
+ }
+ },
+ "tables": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/tableType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. LAW custom tables to be deployed."
+ }
+ },
+ "gallerySolutions": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/gallerySolutionType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of gallerySolutions to be created in the log analytics workspace."
+ }
+ },
+ "onboardWorkspaceToSentinel": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Onboard the Log Analytics Workspace to Sentinel. Requires 'SecurityInsights' solution to be in gallerySolutions."
+ }
+ },
+ "dataRetention": {
+ "type": "int",
+ "defaultValue": 365,
+ "minValue": 0,
+ "maxValue": 730,
+ "metadata": {
+ "description": "Optional. Number of days data will be retained for."
+ }
+ },
+ "dailyQuotaGb": {
+ "type": "int",
+ "defaultValue": -1,
+ "minValue": -1,
+ "metadata": {
+ "description": "Optional. The workspace daily quota for ingestion."
+ }
+ },
+ "publicNetworkAccessForIngestion": {
+ "type": "string",
+ "defaultValue": "Enabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. The network access type for accessing Log Analytics ingestion."
+ }
+ },
+ "publicNetworkAccessForQuery": {
+ "type": "string",
+ "defaultValue": "Enabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. The network access type for accessing Log Analytics query."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource. Only one type of identity is supported: system-assigned or user-assigned, but not both."
+ }
+ },
+ "features": {
+ "$ref": "#/definitions/workspaceFeaturesType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The workspace features."
+ }
+ },
+ "replication": {
+ "$ref": "#/definitions/workspaceReplicationType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The workspace replication properties."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the service."
+ }
+ },
+ "forceCmkForQuery": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Indicates whether customer managed storage is mandatory for query management."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.OperationalInsights/workspaces@2025-02-01#properties/tags"
+ },
+ "description": "Optional. Tags of the resource."
+ },
+ "nullable": true
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "formattedRoleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]",
+ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]"
+ }
+ ],
+ "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()), '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')]",
+ "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]",
+ "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]",
+ "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]",
+ "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]",
+ "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')]",
+ "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]",
+ "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]",
+ "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.operationalinsights-workspace.{0}.{1}', replace('0.12.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"
+ }
+ }
+ }
+ }
+ },
+ "logAnalyticsWorkspace": {
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "features": {
+ "searchVersion": 1,
+ "enableLogAccessUsingOnlyResourcePermissions": "[coalesce(tryGet(parameters('features'), 'enableLogAccessUsingOnlyResourcePermissions'), false())]",
+ "disableLocalAuth": "[coalesce(tryGet(parameters('features'), 'disableLocalAuth'), true())]",
+ "enableDataExport": "[tryGet(parameters('features'), 'enableDataExport')]",
+ "immediatePurgeDataOn30Days": "[tryGet(parameters('features'), 'immediatePurgeDataOn30Days')]"
+ },
+ "sku": {
+ "name": "[parameters('skuName')]",
+ "capacityReservationLevel": "[if(equals(parameters('skuName'), 'CapacityReservation'), parameters('skuCapacityReservationLevel'), null())]"
+ },
+ "retentionInDays": "[parameters('dataRetention')]",
+ "workspaceCapping": {
+ "dailyQuotaGb": "[parameters('dailyQuotaGb')]"
+ },
+ "publicNetworkAccessForIngestion": "[parameters('publicNetworkAccessForIngestion')]",
+ "publicNetworkAccessForQuery": "[parameters('publicNetworkAccessForQuery')]",
+ "forceCmkForQuery": "[parameters('forceCmkForQuery')]",
+ "replication": "[parameters('replication')]"
+ },
+ "identity": "[variables('identity')]"
+ },
+ "logAnalyticsWorkspace_diagnosticSettings": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.OperationalInsights/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": "[if(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'useThisWorkspace'), false()), resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), 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": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_sentinelOnboarding": {
+ "condition": "[and(not(empty(filter(coalesce(parameters('gallerySolutions'), createArray()), lambda('item', startsWith(lambdaVariables('item').name, 'SecurityInsights'))))), parameters('onboardWorkspaceToSentinel'))]",
+ "type": "Microsoft.SecurityInsights/onboardingStates",
+ "apiVersion": "2024-03-01",
+ "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]",
+ "name": "default",
+ "properties": {},
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_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.OperationalInsights/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": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_roleAssignments": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.OperationalInsights/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": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_storageInsightConfigs": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_storageInsightConfigs",
+ "count": "[length(coalesce(parameters('storageInsightsConfigs'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-StorageInsightsConfig-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "containers": {
+ "value": "[tryGet(coalesce(parameters('storageInsightsConfigs'), createArray())[copyIndex()], 'containers')]"
+ },
+ "tables": {
+ "value": "[tryGet(coalesce(parameters('storageInsightsConfigs'), createArray())[copyIndex()], 'tables')]"
+ },
+ "storageAccountResourceId": {
+ "value": "[coalesce(parameters('storageInsightsConfigs'), createArray())[copyIndex()].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.36.1.42791",
+ "templateHash": "1306323182548882150"
+ },
+ "name": "Log Analytics Workspace Storage Insight Configs",
+ "description": "This module deploys a Log Analytics Workspace Storage Insight Config."
+ },
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "[format('{0}-stinsconfig', last(split(parameters('storageAccountResourceId'), '/')))]",
+ "metadata": {
+ "description": "Optional. The name of the storage insights config."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Azure Resource Manager ID of the storage account resource."
+ }
+ },
+ "containers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The names of the blob containers that the workspace should read."
+ }
+ },
+ "tables": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The names of the Azure tables that the workspace should read."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs@2025-02-01#properties/tags"
+ },
+ "description": "Optional. Tags to configure in the resource."
+ },
+ "nullable": true
+ }
+ },
+ "resources": {
+ "storageAccount": {
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[last(split(parameters('storageAccountResourceId'), '/'))]"
+ },
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('logAnalyticsWorkspaceName')]"
+ },
+ "storageinsightconfig": {
+ "type": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "containers": "[parameters('containers')]",
+ "tables": "[parameters('tables')]",
+ "storageAccount": {
+ "id": "[parameters('storageAccountResourceId')]",
+ "key": "[listKeys('storageAccount', '2024-01-01').keys[0].value]"
+ }
+ }
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed storage insights configuration."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/storageInsightConfigs', parameters('logAnalyticsWorkspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group where the storage insight configuration is deployed."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the storage insights configuration."
+ },
+ "value": "[parameters('name')]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_linkedServices": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_linkedServices",
+ "count": "[length(coalesce(parameters('linkedServices'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-LinkedService-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('linkedServices'), createArray())[copyIndex()].name]"
+ },
+ "resourceId": {
+ "value": "[tryGet(coalesce(parameters('linkedServices'), createArray())[copyIndex()], 'resourceId')]"
+ },
+ "writeAccessResourceId": {
+ "value": "[tryGet(coalesce(parameters('linkedServices'), createArray())[copyIndex()], 'writeAccessResourceId')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": "5230241501765697269"
+ },
+ "name": "Log Analytics Workspace Linked Services",
+ "description": "This module deploys a Log Analytics Workspace Linked Service."
+ },
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the link."
+ }
+ },
+ "resourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access."
+ }
+ },
+ "writeAccessResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.OperationalInsights/workspaces/linkedServices@2025-02-01#properties/tags"
+ },
+ "description": "Optional. Tags to configure in the resource."
+ },
+ "nullable": true
+ }
+ },
+ "resources": {
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('logAnalyticsWorkspaceName')]"
+ },
+ "linkedService": {
+ "type": "Microsoft.OperationalInsights/workspaces/linkedServices",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "resourceId": "[parameters('resourceId')]",
+ "writeAccessResourceId": "[parameters('writeAccessResourceId')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed linked service."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed linked service."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/linkedServices', parameters('logAnalyticsWorkspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group where the linked service is deployed."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_linkedStorageAccounts": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_linkedStorageAccounts",
+ "count": "[length(coalesce(parameters('linkedStorageAccounts'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-LinkedStorageAccount-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('linkedStorageAccounts'), createArray())[copyIndex()].name]"
+ },
+ "storageAccountIds": {
+ "value": "[coalesce(parameters('linkedStorageAccounts'), createArray())[copyIndex()].storageAccountIds]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": "10372135754202496594"
+ },
+ "name": "Log Analytics Workspace Linked Storage Accounts",
+ "description": "This module deploys a Log Analytics Workspace Linked Storage Account."
+ },
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "allowedValues": [
+ "Query",
+ "Alerts",
+ "CustomLogs",
+ "AzureWatson"
+ ],
+ "metadata": {
+ "description": "Required. Name of the link."
+ }
+ },
+ "storageAccountIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minLength": 1,
+ "metadata": {
+ "description": "Required. Linked storage accounts resources Ids."
+ }
+ }
+ },
+ "resources": {
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('logAnalyticsWorkspaceName')]"
+ },
+ "linkedStorageAccount": {
+ "type": "Microsoft.OperationalInsights/workspaces/linkedStorageAccounts",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]",
+ "properties": {
+ "storageAccountIds": "[parameters('storageAccountIds')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed linked storage account."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed linked storage account."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/linkedStorageAccounts', parameters('logAnalyticsWorkspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group where the linked storage account is deployed."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_savedSearches": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_savedSearches",
+ "count": "[length(coalesce(parameters('savedSearches'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-SavedSearch-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[format('{0}{1}', coalesce(parameters('savedSearches'), createArray())[copyIndex()].name, uniqueString(deployment().name))]"
+ },
+ "etag": {
+ "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'etag')]"
+ },
+ "displayName": {
+ "value": "[coalesce(parameters('savedSearches'), createArray())[copyIndex()].displayName]"
+ },
+ "category": {
+ "value": "[coalesce(parameters('savedSearches'), createArray())[copyIndex()].category]"
+ },
+ "query": {
+ "value": "[coalesce(parameters('savedSearches'), createArray())[copyIndex()].query]"
+ },
+ "functionAlias": {
+ "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'functionAlias')]"
+ },
+ "functionParameters": {
+ "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'functionParameters')]"
+ },
+ "tags": {
+ "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'tags')]"
+ },
+ "version": {
+ "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'version')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": "9015459905306126128"
+ },
+ "name": "Log Analytics Workspace Saved Searches",
+ "description": "This module deploys a Log Analytics Workspace Saved Search."
+ },
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the saved search."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Display name for the search."
+ }
+ },
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Query category."
+ }
+ },
+ "query": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Kusto Query to be stored."
+ }
+ },
+ "tags": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.OperationalInsights/workspaces/savedSearches@2025-02-01#properties/properties/properties/tags"
+ },
+ "description": "Optional. Tags to configure in the resource."
+ },
+ "nullable": true
+ },
+ "functionAlias": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The function alias if query serves as a function."
+ }
+ },
+ "functionParameters": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The optional function parameters if query serves as a function. Value should be in the following format: \"param-name1:type1 = default_value1, param-name2:type2 = default_value2\". For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions."
+ }
+ },
+ "version": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The version number of the query language."
+ }
+ },
+ "etag": {
+ "type": "string",
+ "defaultValue": "*",
+ "metadata": {
+ "description": "Optional. The ETag of the saved search. To override an existing saved search, use \"*\" or specify the current Etag."
+ }
+ }
+ },
+ "resources": {
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('logAnalyticsWorkspaceName')]"
+ },
+ "savedSearch": {
+ "type": "Microsoft.OperationalInsights/workspaces/savedSearches",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]",
+ "properties": {
+ "etag": "[parameters('etag')]",
+ "tags": "[coalesce(parameters('tags'), createArray())]",
+ "displayName": "[parameters('displayName')]",
+ "category": "[parameters('category')]",
+ "query": "[parameters('query')]",
+ "functionAlias": "[parameters('functionAlias')]",
+ "functionParameters": "[parameters('functionParameters')]",
+ "version": "[parameters('version')]"
+ }
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed saved search."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('logAnalyticsWorkspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group where the saved search is deployed."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed saved search."
+ },
+ "value": "[parameters('name')]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace",
+ "logAnalyticsWorkspace_linkedStorageAccounts"
+ ]
+ },
+ "logAnalyticsWorkspace_dataExports": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_dataExports",
+ "count": "[length(coalesce(parameters('dataExports'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-DataExport-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "workspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('dataExports'), createArray())[copyIndex()].name]"
+ },
+ "destination": {
+ "value": "[tryGet(coalesce(parameters('dataExports'), createArray())[copyIndex()], 'destination')]"
+ },
+ "enable": {
+ "value": "[tryGet(coalesce(parameters('dataExports'), createArray())[copyIndex()], 'enable')]"
+ },
+ "tableNames": {
+ "value": "[tryGet(coalesce(parameters('dataExports'), createArray())[copyIndex()], 'tableNames')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": "8586520532175356447"
+ },
+ "name": "Log Analytics Workspace Data Exports",
+ "description": "This module deploys a Log Analytics Workspace Data Export."
+ },
+ "definitions": {
+ "destinationType": {
+ "type": "object",
+ "properties": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The destination resource ID."
+ }
+ },
+ "metaData": {
+ "type": "object",
+ "properties": {
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Allows to define an Event Hub name. Not applicable when destination is Storage Account."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The destination metadata."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The data export destination properties."
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 63,
+ "metadata": {
+ "description": "Required. The data export rule name."
+ }
+ },
+ "workspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment."
+ }
+ },
+ "destination": {
+ "$ref": "#/definitions/destinationType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Destination properties."
+ }
+ },
+ "enable": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Active when enabled."
+ }
+ },
+ "tableNames": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minLength": 1,
+ "metadata": {
+ "description": "Required. An array of tables to export, for example: ['Heartbeat', 'SecurityEvent']."
+ }
+ }
+ },
+ "resources": {
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('workspaceName')]"
+ },
+ "dataExport": {
+ "type": "Microsoft.OperationalInsights/workspaces/dataExports",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]",
+ "properties": {
+ "destination": "[parameters('destination')]",
+ "enable": "[parameters('enable')]",
+ "tableNames": "[parameters('tableNames')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the data export."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the data export."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/dataExports', parameters('workspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the data export was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_dataSources": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_dataSources",
+ "count": "[length(coalesce(parameters('dataSources'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-DataSource-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('dataSources'), createArray())[copyIndex()].name]"
+ },
+ "kind": {
+ "value": "[coalesce(parameters('dataSources'), createArray())[copyIndex()].kind]"
+ },
+ "linkedResourceId": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'linkedResourceId')]"
+ },
+ "eventLogName": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'eventLogName')]"
+ },
+ "eventTypes": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'eventTypes')]"
+ },
+ "objectName": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'objectName')]"
+ },
+ "instanceName": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'instanceName')]"
+ },
+ "intervalSeconds": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'intervalSeconds')]"
+ },
+ "counterName": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'counterName')]"
+ },
+ "state": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'state')]"
+ },
+ "syslogName": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'syslogName')]"
+ },
+ "syslogSeverities": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'syslogSeverities')]"
+ },
+ "performanceCounters": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'performanceCounters')]"
+ },
+ "tags": {
+ "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], '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": "8336916453932906250"
+ },
+ "name": "Log Analytics Workspace Datasources",
+ "description": "This module deploys a Log Analytics Workspace Data Source."
+ },
+ "parameters": {
+ "logAnalyticsWorkspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the data source."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "defaultValue": "AzureActivityLog",
+ "allowedValues": [
+ "AzureActivityLog",
+ "WindowsEvent",
+ "WindowsPerformanceCounter",
+ "IISLogs",
+ "LinuxSyslog",
+ "LinuxSyslogCollection",
+ "LinuxPerformanceObject",
+ "LinuxPerformanceCollection"
+ ],
+ "metadata": {
+ "description": "Optional. The kind of the data source."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.OperationalInsights/workspaces/dataSources@2025-02-01#properties/tags"
+ },
+ "description": "Optional. Tags to configure in the resource."
+ },
+ "nullable": true
+ },
+ "linkedResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the resource to be linked."
+ }
+ },
+ "eventLogName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Windows event log name to configure when kind is WindowsEvent."
+ }
+ },
+ "eventTypes": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. Windows event types to configure when kind is WindowsEvent."
+ }
+ },
+ "objectName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject."
+ }
+ },
+ "instanceName": {
+ "type": "string",
+ "defaultValue": "*",
+ "metadata": {
+ "description": "Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject."
+ }
+ },
+ "intervalSeconds": {
+ "type": "int",
+ "defaultValue": 60,
+ "metadata": {
+ "description": "Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject."
+ }
+ },
+ "performanceCounters": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. List of counters to configure when the kind is LinuxPerformanceObject."
+ }
+ },
+ "counterName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Counter name to configure when kind is WindowsPerformanceCounter."
+ }
+ },
+ "state": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection."
+ }
+ },
+ "syslogName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. System log to configure when kind is LinuxSyslog."
+ }
+ },
+ "syslogSeverities": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. Severities to configure when kind is LinuxSyslog."
+ }
+ }
+ },
+ "resources": {
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('logAnalyticsWorkspaceName')]"
+ },
+ "dataSource": {
+ "type": "Microsoft.OperationalInsights/workspaces/dataSources",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]",
+ "kind": "[parameters('kind')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "linkedResourceId": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'AzureActivityLog')), parameters('linkedResourceId'), null())]",
+ "eventLogName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsEvent')), parameters('eventLogName'), null())]",
+ "eventTypes": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsEvent')), parameters('eventTypes'), null())]",
+ "objectName": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('objectName'), null())]",
+ "instanceName": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('instanceName'), null())]",
+ "intervalSeconds": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('intervalSeconds'), null())]",
+ "counterName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsPerformanceCounter')), parameters('counterName'), null())]",
+ "state": "[if(and(not(empty(parameters('kind'))), or(or(equals(parameters('kind'), 'IISLogs'), equals(parameters('kind'), 'LinuxSyslogCollection')), equals(parameters('kind'), 'LinuxPerformanceCollection'))), parameters('state'), null())]",
+ "syslogName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'LinuxSyslog')), parameters('syslogName'), null())]",
+ "syslogSeverities": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'LinuxSyslog'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('syslogSeverities'), null())]",
+ "performanceCounters": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'LinuxPerformanceObject')), parameters('performanceCounters'), null())]"
+ }
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed data source."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/dataSources', parameters('logAnalyticsWorkspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group where the data source is deployed."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed data source."
+ },
+ "value": "[parameters('name')]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_tables": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_tables",
+ "count": "[length(coalesce(parameters('tables'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-Table-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "workspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('tables'), createArray())[copyIndex()].name]"
+ },
+ "plan": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'plan')]"
+ },
+ "schema": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'schema')]"
+ },
+ "retentionInDays": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'retentionInDays')]"
+ },
+ "totalRetentionInDays": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'totalRetentionInDays')]"
+ },
+ "restoredLogs": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'restoredLogs')]"
+ },
+ "searchResults": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'searchResults')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(coalesce(parameters('tables'), 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.36.1.42791",
+ "templateHash": "315390662258960765"
+ },
+ "name": "Log Analytics Workspace Tables",
+ "description": "This module deploys a Log Analytics Workspace Table."
+ },
+ "definitions": {
+ "restoredLogsType": {
+ "type": "object",
+ "properties": {
+ "sourceTable": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The table to restore data from."
+ }
+ },
+ "startRestoreTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to start the restore from (UTC)."
+ }
+ },
+ "endRestoreTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to end the restore by (UTC)."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The parameters of the restore operation that initiated the table."
+ }
+ },
+ "schemaType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The table name."
+ }
+ },
+ "columns": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/columnType"
+ },
+ "metadata": {
+ "description": "Required. A list of table custom columns."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The table description."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The table display name."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The table schema."
+ }
+ },
+ "columnType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The column name."
+ }
+ },
+ "type": {
+ "type": "string",
+ "allowedValues": [
+ "boolean",
+ "dateTime",
+ "dynamic",
+ "guid",
+ "int",
+ "long",
+ "real",
+ "string"
+ ],
+ "metadata": {
+ "description": "Required. The column type."
+ }
+ },
+ "dataTypeHint": {
+ "type": "string",
+ "allowedValues": [
+ "armPath",
+ "guid",
+ "ip",
+ "uri"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The column data type logical hint."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The column description."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Column display name."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The parameters of the table column."
+ }
+ },
+ "searchResultsType": {
+ "type": "object",
+ "properties": {
+ "query": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The search job query."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The search description."
+ }
+ },
+ "limit": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Limit the search job to return up to specified number of rows."
+ }
+ },
+ "startSearchTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to start the search from (UTC)."
+ }
+ },
+ "endSearchTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The timestamp to end the search by (UTC)."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The parameters of the search job that initiated the table."
+ }
+ },
+ "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 table."
+ }
+ },
+ "workspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment."
+ }
+ },
+ "plan": {
+ "type": "string",
+ "defaultValue": "Analytics",
+ "allowedValues": [
+ "Basic",
+ "Analytics"
+ ],
+ "metadata": {
+ "description": "Optional. Instruct the system how to handle and charge the logs ingested to this table."
+ }
+ },
+ "restoredLogs": {
+ "$ref": "#/definitions/restoredLogsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Restore parameters."
+ }
+ },
+ "retentionInDays": {
+ "type": "int",
+ "defaultValue": -1,
+ "minValue": -1,
+ "maxValue": 730,
+ "metadata": {
+ "description": "Optional. The table retention in days, between 4 and 730. Setting this property to -1 will default to the workspace retention."
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/schemaType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Table's schema."
+ }
+ },
+ "searchResults": {
+ "$ref": "#/definitions/searchResultsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Parameters of the search job that initiated this table."
+ }
+ },
+ "totalRetentionInDays": {
+ "type": "int",
+ "defaultValue": -1,
+ "minValue": -1,
+ "maxValue": 2555,
+ "metadata": {
+ "description": "Optional. The table total retention in days, between 4 and 2555. Setting this property to -1 will default to table retention."
+ }
+ },
+ "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')]",
+ "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]",
+ "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]",
+ "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]",
+ "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]",
+ "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": {
+ "workspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2025-02-01",
+ "name": "[parameters('workspaceName')]"
+ },
+ "table": {
+ "type": "Microsoft.OperationalInsights/workspaces/tables",
+ "apiVersion": "2025-02-01",
+ "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]",
+ "properties": {
+ "plan": "[parameters('plan')]",
+ "restoredLogs": "[parameters('restoredLogs')]",
+ "retentionInDays": "[parameters('retentionInDays')]",
+ "schema": "[parameters('schema')]",
+ "searchResults": "[parameters('searchResults')]",
+ "totalRetentionInDays": "[parameters('totalRetentionInDays')]"
+ }
+ },
+ "table_roleAssignments": {
+ "copy": {
+ "name": "table_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}/tables/{1}', parameters('workspaceName'), parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), 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 table."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the table."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the table was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "logAnalyticsWorkspace_solutions": {
+ "copy": {
+ "name": "logAnalyticsWorkspace_solutions",
+ "count": "[length(coalesce(parameters('gallerySolutions'), createArray()))]"
+ },
+ "condition": "[not(empty(parameters('gallerySolutions')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-LAW-Solution-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[coalesce(parameters('gallerySolutions'), createArray())[copyIndex()].name]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "logAnalyticsWorkspaceName": {
+ "value": "[parameters('name')]"
+ },
+ "plan": {
+ "value": "[coalesce(parameters('gallerySolutions'), createArray())[copyIndex()].plan]"
+ },
+ "enableTelemetry": {
+ "value": "[variables('enableReferencedModulesTelemetry')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": "10255889523646649592"
+ },
+ "name": "Operations Management Solutions",
+ "description": "This module deploys an Operations Management Solution.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "solutionPlanType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the solution to be created.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, it can be anything.\nThe solution type is case-sensitive.\nIf not provided, the value of the `name` parameter will be used."
+ }
+ },
+ "product": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The product name of the deployed solution.\nFor Microsoft published gallery solution it should be `OMSGallery/{solutionType}`, for example `OMSGallery/AntiMalware`.\nFor a third party solution, it can be anything.\nThis is case sensitive."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`, which is the default value."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the solution.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, the name should be in the pattern: `SolutionType[WorkspaceName]`, for example `MySolution[contoso-Logs]`.\nThe solution type is case-sensitive."
+ }
+ },
+ "plan": {
+ "$ref": "#/definitions/solutionPlanType",
+ "metadata": {
+ "description": "Required. Plan for solution object supported by the OperationsManagement resource provider."
+ }
+ },
+ "logAnalyticsWorkspaceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Log Analytics workspace where the solution will be deployed/enabled."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.operationsmanagement-solution.{0}.{1}', replace('0.3.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"
+ }
+ }
+ }
+ }
+ },
+ "logAnalyticsWorkspace": {
+ "existing": true,
+ "type": "Microsoft.OperationalInsights/workspaces",
+ "apiVersion": "2021-06-01",
+ "name": "[parameters('logAnalyticsWorkspaceName')]"
+ },
+ "solution": {
+ "type": "Microsoft.OperationsManagement/solutions",
+ "apiVersion": "2015-11-01-preview",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "properties": {
+ "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]"
+ },
+ "plan": {
+ "name": "[coalesce(tryGet(parameters('plan'), 'name'), parameters('name'))]",
+ "promotionCode": "",
+ "product": "[parameters('plan').product]",
+ "publisher": "[coalesce(tryGet(parameters('plan'), 'publisher'), 'Microsoft')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed solution."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed solution."
+ },
+ "value": "[resourceId('Microsoft.OperationsManagement/solutions', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group where the solution is deployed."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('solution', '2015-11-01-preview', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed log analytics workspace."
+ },
+ "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group of the deployed log analytics workspace."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed log analytics workspace."
+ },
+ "value": "[parameters('name')]"
+ },
+ "logAnalyticsWorkspaceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The ID associated with the workspace."
+ },
+ "value": "[reference('logAnalyticsWorkspace').customerId]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('logAnalyticsWorkspace', '2025-02-01', 'full').location]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[tryGet(tryGet(reference('logAnalyticsWorkspace', '2025-02-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "primarySharedKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary shared key of the log analytics workspace."
+ },
+ "value": "[listKeys('logAnalyticsWorkspace', '2025-02-01').primarySharedKey]"
+ },
+ "secondarySharedKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary shared key of the log analytics workspace."
+ },
+ "value": "[listKeys('logAnalyticsWorkspace', '2025-02-01').secondarySharedKey]"
+ }
+ }
+ }
+ }
+ },
+ "applicationInsights": {
+ "condition": "[parameters('enableMonitoring')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.insights.component.{0}', variables('applicationInsightsResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('applicationInsightsResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "retentionInDays": {
+ "value": 365
+ },
+ "kind": {
+ "value": "web"
+ },
+ "disableIpMasking": {
+ "value": false
+ },
+ "flowType": {
+ "value": "Bluefield"
+ },
+ "workspaceResourceId": "[if(parameters('enableMonitoring'), if(variables('useExistingLogAnalytics'), createObject('value', parameters('existingLogAnalyticsWorkspaceId')), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.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.33.93.31351",
+ "templateHash": "5735496719243704506"
+ },
+ "name": "Application Insights",
+ "description": "This component deploys an Application Insights instance."
+ },
+ "definitions": {
+ "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.3.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.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.3.0"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Application Insights."
+ }
+ },
+ "applicationType": {
+ "type": "string",
+ "defaultValue": "web",
+ "allowedValues": [
+ "web",
+ "other"
+ ],
+ "metadata": {
+ "description": "Optional. Application type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the log analytics workspace which the data will be ingested to. This property is required to create an application with this API version. Applications from older versions will not have this property."
+ }
+ },
+ "disableIpMasking": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Disable IP masking. Default value is set to true."
+ }
+ },
+ "disableLocalAuth": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Disable Non-AAD based Auth. Default value is set to false."
+ }
+ },
+ "forceCustomerStorageForProfiler": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Force users to create their own storage account for profiler and debugger."
+ }
+ },
+ "linkedStorageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Linked storage account resource ID."
+ }
+ },
+ "publicNetworkAccessForIngestion": {
+ "type": "string",
+ "defaultValue": "Enabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. The network access type for accessing Application Insights ingestion. - Enabled or Disabled."
+ }
+ },
+ "publicNetworkAccessForQuery": {
+ "type": "string",
+ "defaultValue": "Enabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. The network access type for accessing Application Insights query. - Enabled or Disabled."
+ }
+ },
+ "retentionInDays": {
+ "type": "int",
+ "defaultValue": 365,
+ "allowedValues": [
+ 30,
+ 60,
+ 90,
+ 120,
+ 180,
+ 270,
+ 365,
+ 550,
+ 730
+ ],
+ "metadata": {
+ "description": "Optional. Retention period in days."
+ }
+ },
+ "samplingPercentage": {
+ "type": "int",
+ "defaultValue": 100,
+ "minValue": 0,
+ "maxValue": 100,
+ "metadata": {
+ "description": "Optional. Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry."
+ }
+ },
+ "flowType": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Used by the Application Insights system to determine what kind of flow this component was created by. This is to be set to 'Bluefield' when creating/updating a component via the REST API."
+ }
+ },
+ "requestSource": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Describes what tool created this Application Insights component. Customers using this API should set this to the default 'rest'."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The kind of application that this component refers to, used to customize UI. This value is a freeform string, values should typically be one of the following: web, ios, other, store, java, phone."
+ }
+ },
+ "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 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."
+ }
+ }
+ },
+ "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')]",
+ "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]",
+ "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]",
+ "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]",
+ "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.insights-component.{0}.{1}', replace('0.6.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"
+ }
+ }
+ }
+ }
+ },
+ "appInsights": {
+ "type": "Microsoft.Insights/components",
+ "apiVersion": "2020-02-02",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "kind": "[parameters('kind')]",
+ "properties": {
+ "Application_Type": "[parameters('applicationType')]",
+ "DisableIpMasking": "[parameters('disableIpMasking')]",
+ "DisableLocalAuth": "[parameters('disableLocalAuth')]",
+ "ForceCustomerStorageForProfiler": "[parameters('forceCustomerStorageForProfiler')]",
+ "WorkspaceResourceId": "[parameters('workspaceResourceId')]",
+ "publicNetworkAccessForIngestion": "[parameters('publicNetworkAccessForIngestion')]",
+ "publicNetworkAccessForQuery": "[parameters('publicNetworkAccessForQuery')]",
+ "RetentionInDays": "[parameters('retentionInDays')]",
+ "SamplingPercentage": "[parameters('samplingPercentage')]",
+ "Flow_Type": "[parameters('flowType')]",
+ "Request_Source": "[parameters('requestSource')]"
+ }
+ },
+ "appInsights_roleAssignments": {
+ "copy": {
+ "name": "appInsights_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/components', 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": [
+ "appInsights"
+ ]
+ },
+ "appInsights_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.Insights/components/{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": [
+ "appInsights"
+ ]
+ },
+ "appInsights_diagnosticSettings": {
+ "copy": {
+ "name": "appInsights_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.Insights/components/{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": [
+ "appInsights"
+ ]
+ },
+ "linkedStorageAccount": {
+ "condition": "[not(empty(parameters('linkedStorageAccountResourceId')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-appInsights-linkedStorageAccount', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "appInsightsName": {
+ "value": "[parameters('name')]"
+ },
+ "storageAccountResourceId": {
+ "value": "[coalesce(parameters('linkedStorageAccountResourceId'), '')]"
+ }
+ },
+ "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": "10861379689695100897"
+ },
+ "name": "Application Insights Linked Storage Account",
+ "description": "This component deploys an Application Insights Linked Storage Account."
+ },
+ "parameters": {
+ "appInsightsName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Application Insights instance. Required if the template is used in a standalone deployment."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Linked storage account resource ID."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "microsoft.insights/components/linkedStorageAccounts",
+ "apiVersion": "2020-03-01-preview",
+ "name": "[format('{0}/{1}', parameters('appInsightsName'), 'ServiceProfiler')]",
+ "properties": {
+ "linkedStorageAccount": "[parameters('storageAccountResourceId')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Linked Storage Account."
+ },
+ "value": "ServiceProfiler"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Linked Storage Account."
+ },
+ "value": "[resourceId('microsoft.insights/components/linkedStorageAccounts', parameters('appInsightsName'), 'ServiceProfiler')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the agent pool was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "appInsights"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the application insights component."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the application insights component."
+ },
+ "value": "[resourceId('Microsoft.Insights/components', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the application insights component was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "applicationId": {
+ "type": "string",
+ "metadata": {
+ "description": "The application ID of the application insights component."
+ },
+ "value": "[reference('appInsights').AppId]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('appInsights', '2020-02-02', 'full').location]"
+ },
+ "instrumentationKey": {
+ "type": "string",
+ "metadata": {
+ "description": "Application Insights Instrumentation key. A read-only value that applications can use to identify the destination for all telemetry sent to Azure Application Insights. This value will be supplied upon construction of each new Application Insights component."
+ },
+ "value": "[reference('appInsights').InstrumentationKey]"
+ },
+ "connectionString": {
+ "type": "string",
+ "metadata": {
+ "description": "Application Insights Connection String."
+ },
+ "value": "[reference('appInsights').ConnectionString]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "userAssignedIdentity": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.managed-identity.user-assigned-identity.{0}', variables('userAssignedIdentityResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('userAssignedIdentityResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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]"
+ }
+ }
+ }
+ }
+ },
+ "virtualNetwork": {
+ "condition": "[parameters('enablePrivateNetworking')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('module.virtualNetwork.{0}', variables('solutionSuffix')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[format('vnet-{0}', variables('solutionSuffix'))]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "addressPrefixes": {
+ "value": [
+ "10.0.0.0/8"
+ ]
+ },
+ "logAnalyticsWorkspaceId": "[if(variables('useExistingLogAnalytics'), createObject('value', parameters('existingLogAnalyticsWorkspaceId')), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]",
+ "resourceSuffix": {
+ "value": "[variables('solutionSuffix')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.41.2.15936",
+ "templateHash": "8667922205584012198"
+ }
+ },
+ "definitions": {
+ "subnetOutputType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the subnet."
+ }
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the subnet."
+ }
+ },
+ "nsgName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The name of the associated network security group, if any."
+ }
+ },
+ "nsgResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The resource ID of the associated network security group, if any."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Custom type definition for subnet resource information as output"
+ }
+ },
+ "subnetType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Name of the subnet resource."
+ }
+ },
+ "addressPrefixes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "metadata": {
+ "description": "Required. Prefixes for the subnet."
+ }
+ },
+ "delegation": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The delegation to enable on the subnet."
+ }
+ },
+ "privateEndpointNetworkPolicies": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled",
+ "NetworkSecurityGroupEnabled",
+ "RouteTableEnabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. enable or disable apply network policies on private endpoint in the subnet."
+ }
+ },
+ "privateLinkServiceNetworkPolicies": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable apply network policies on private link service in the subnet."
+ }
+ },
+ "networkSecurityGroup": {
+ "$ref": "#/definitions/networkSecurityGroupType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Network Security Group configuration for the subnet."
+ }
+ },
+ "routeTableResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the route table to assign to the subnet."
+ }
+ },
+ "serviceEndpointPolicies": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. An array of service endpoint policies."
+ }
+ },
+ "serviceEndpoints": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The service endpoints to enable on the subnet."
+ }
+ },
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Custom type definition for subnet configuration"
+ }
+ },
+ "networkSecurityGroupType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the network security group."
+ }
+ },
+ "securityRules": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ },
+ "metadata": {
+ "description": "Required. The security rules for the network security group."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Custom type definition for network security group configuration"
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the virtual network."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Azure region to deploy resources."
+ }
+ },
+ "addressPrefixes": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network."
+ }
+ },
+ "subnets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/subnetType"
+ },
+ "defaultValue": [
+ {
+ "name": "backend",
+ "addressPrefixes": [
+ "10.0.0.0/27"
+ ],
+ "networkSecurityGroup": {
+ "name": "nsg-backend",
+ "securityRules": [
+ {
+ "name": "deny-hop-outbound",
+ "properties": {
+ "access": "Deny",
+ "destinationAddressPrefix": "*",
+ "destinationPortRanges": [
+ "22",
+ "3389"
+ ],
+ "direction": "Outbound",
+ "priority": 200,
+ "protocol": "Tcp",
+ "sourceAddressPrefix": "VirtualNetwork",
+ "sourcePortRange": "*"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "name": "containers",
+ "addressPrefixes": [
+ "10.0.2.0/23"
+ ],
+ "delegation": "Microsoft.App/environments",
+ "privateEndpointNetworkPolicies": "Enabled",
+ "privateLinkServiceNetworkPolicies": "Enabled",
+ "networkSecurityGroup": {
+ "name": "nsg-containers",
+ "securityRules": [
+ {
+ "name": "deny-hop-outbound",
+ "properties": {
+ "access": "Deny",
+ "destinationAddressPrefix": "*",
+ "destinationPortRanges": [
+ "22",
+ "3389"
+ ],
+ "direction": "Outbound",
+ "priority": 200,
+ "protocol": "Tcp",
+ "sourceAddressPrefix": "VirtualNetwork",
+ "sourcePortRange": "*"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "name": "webserverfarm",
+ "addressPrefixes": [
+ "10.0.4.0/27"
+ ],
+ "delegation": "Microsoft.Web/serverfarms",
+ "privateEndpointNetworkPolicies": "Enabled",
+ "privateLinkServiceNetworkPolicies": "Enabled",
+ "networkSecurityGroup": {
+ "name": "nsg-webserverfarm",
+ "securityRules": [
+ {
+ "name": "deny-hop-outbound",
+ "properties": {
+ "access": "Deny",
+ "destinationAddressPrefix": "*",
+ "destinationPortRanges": [
+ "22",
+ "3389"
+ ],
+ "direction": "Outbound",
+ "priority": 200,
+ "protocol": "Tcp",
+ "sourceAddressPrefix": "VirtualNetwork",
+ "sourcePortRange": "*"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "name": "administration",
+ "addressPrefixes": [
+ "10.0.0.32/27"
+ ],
+ "networkSecurityGroup": {
+ "name": "nsg-administration",
+ "securityRules": [
+ {
+ "name": "deny-hop-outbound",
+ "properties": {
+ "access": "Deny",
+ "destinationAddressPrefix": "*",
+ "destinationPortRanges": [
+ "22",
+ "3389"
+ ],
+ "direction": "Outbound",
+ "priority": 200,
+ "protocol": "Tcp",
+ "sourceAddressPrefix": "VirtualNetwork",
+ "sourcePortRange": "*"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "name": "AzureBastionSubnet",
+ "addressPrefixes": [
+ "10.0.0.64/26"
+ ],
+ "networkSecurityGroup": {
+ "name": "nsg-bastion",
+ "securityRules": [
+ {
+ "name": "AllowGatewayManager",
+ "properties": {
+ "access": "Allow",
+ "direction": "Inbound",
+ "priority": 2702,
+ "protocol": "*",
+ "sourcePortRange": "*",
+ "destinationPortRange": "443",
+ "sourceAddressPrefix": "GatewayManager",
+ "destinationAddressPrefix": "*"
+ }
+ },
+ {
+ "name": "AllowHttpsInBound",
+ "properties": {
+ "access": "Allow",
+ "direction": "Inbound",
+ "priority": 2703,
+ "protocol": "*",
+ "sourcePortRange": "*",
+ "destinationPortRange": "443",
+ "sourceAddressPrefix": "Internet",
+ "destinationAddressPrefix": "*"
+ }
+ },
+ {
+ "name": "AllowSshRdpOutbound",
+ "properties": {
+ "access": "Allow",
+ "direction": "Outbound",
+ "priority": 100,
+ "protocol": "*",
+ "sourcePortRange": "*",
+ "destinationPortRanges": [
+ "22",
+ "3389"
+ ],
+ "sourceAddressPrefix": "*",
+ "destinationAddressPrefix": "VirtualNetwork"
+ }
+ },
+ {
+ "name": "AllowAzureCloudOutbound",
+ "properties": {
+ "access": "Allow",
+ "direction": "Outbound",
+ "priority": 110,
+ "protocol": "Tcp",
+ "sourcePortRange": "*",
+ "destinationPortRange": "443",
+ "sourceAddressPrefix": "*",
+ "destinationAddressPrefix": "AzureCloud"
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "metadata": {
+ "description": "An array of subnets to be created within the virtual network. Each subnet can have its own configuration and associated Network Security Group (NSG)."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Tags to be applied to the resources."
+ }
+ },
+ "logAnalyticsWorkspaceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Optional. The resource ID of the Log Analytics Workspace to send diagnostic logs to."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "resourceSuffix": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Suffix for resource naming."
+ }
+ }
+ },
+ "resources": {
+ "nsgs": {
+ "copy": {
+ "name": "nsgs",
+ "count": "[length(parameters('subnets'))]",
+ "mode": "serial",
+ "batchSize": 1
+ },
+ "condition": "[not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.network.network-security-group.{0}.{1}', tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name'), parameters('resourceSuffix')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[format('{0}-{1}', tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name'), parameters('resourceSuffix'))]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "securityRules": {
+ "value": "[tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'securityRules')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": "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."
+ }
+ }
+ },
+ "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`."
+ }
+ }
+ }
+ },
+ "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."
+ }
+ }
+ },
+ "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",
+ "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 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"
+ }
+ }
+ }
+ }
+ },
+ "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]"
+ }
+ }
+ }
+ }
+ },
+ "virtualNetwork": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.network.virtual-network.{0}', parameters('name')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "addressPrefixes": {
+ "value": "[parameters('addressPrefixes')]"
+ },
+ "subnets": {
+ "copy": [
+ {
+ "name": "value",
+ "count": "[length(parameters('subnets'))]",
+ "input": "[createObject('name', parameters('subnets')[copyIndex('value')].name, 'addressPrefixes', tryGet(parameters('subnets')[copyIndex('value')], 'addressPrefixes'), 'networkSecurityGroupResourceId', if(not(empty(tryGet(parameters('subnets')[copyIndex('value')], 'networkSecurityGroup'))), reference(format('nsgs[{0}]', copyIndex('value'))).outputs.resourceId.value, null()), 'privateEndpointNetworkPolicies', tryGet(parameters('subnets')[copyIndex('value')], 'privateEndpointNetworkPolicies'), 'privateLinkServiceNetworkPolicies', tryGet(parameters('subnets')[copyIndex('value')], 'privateLinkServiceNetworkPolicies'), 'delegation', tryGet(parameters('subnets')[copyIndex('value')], 'delegation'))]"
+ }
+ ]
+ },
+ "diagnosticSettings": {
+ "value": [
+ {
+ "name": "vnetDiagnostics",
+ "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]",
+ "logCategoriesAndGroups": [
+ {
+ "categoryGroup": "allLogs",
+ "enabled": true
+ }
+ ],
+ "metricCategories": [
+ {
+ "category": "AllMetrics",
+ "enabled": true
+ }
+ ]
+ }
+ ]
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "16195883788906927531"
+ },
+ "name": "Virtual Networks",
+ "description": "This module deploys a Virtual Network (vNet)."
+ },
+ "definitions": {
+ "peeringType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName."
+ }
+ },
+ "remoteVirtualNetworkResourceId": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "allowForwardedTraffic": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "allowGatewayTransit": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false."
+ }
+ },
+ "allowVirtualNetworkAccess": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "doNotVerifyRemoteGateways": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true."
+ }
+ },
+ "useRemoteGateways": {
+ "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."
+ }
+ },
+ "remotePeeringEnabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Deploy the outbound and the inbound peering."
+ }
+ },
+ "remotePeeringName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName."
+ }
+ },
+ "remotePeeringAllowForwardedTraffic": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "remotePeeringAllowGatewayTransit": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false."
+ }
+ },
+ "remotePeeringAllowVirtualNetworkAccess": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ }
+ }
+ },
+ "subnetType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Name of the subnet resource."
+ }
+ },
+ "addressPrefix": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty."
+ }
+ },
+ "addressPrefixes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty."
+ }
+ },
+ "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": "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."
+ }
+ },
+ "applicationGatewayIPConfigurations": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Application gateway IP configurations of virtual network resource."
+ }
+ },
+ "delegation": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The delegation to enable on the subnet."
+ }
+ },
+ "natGatewayResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the NAT Gateway to use for the subnet."
+ }
+ },
+ "networkSecurityGroupResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the network security group to assign to the subnet."
+ }
+ },
+ "privateEndpointNetworkPolicies": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled",
+ "NetworkSecurityGroupEnabled",
+ "RouteTableEnabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. enable or disable apply network policies on private endpoint in the subnet."
+ }
+ },
+ "privateLinkServiceNetworkPolicies": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. enable or disable apply network policies on private link service in the subnet."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "routeTableResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the route table to assign to the subnet."
+ }
+ },
+ "serviceEndpointPolicies": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. An array of service endpoint policies."
+ }
+ },
+ "serviceEndpoints": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The service endpoints to enable on the subnet."
+ }
+ },
+ "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 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."
+ }
+ }
+ }
+ },
+ "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.2.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.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\"."
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the Virtual Network (vNet)."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "addressPrefixes": {
+ "type": "array",
+ "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. 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."
+ }
+ },
+ "virtualNetworkBgpCommunity": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The BGP community associated with the virtual network."
+ }
+ },
+ "subnets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/subnetType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. An Array of subnets to deploy to the Virtual Network."
+ }
+ },
+ "dnsServers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. DNS Servers associated to the Virtual Network."
+ }
+ },
+ "ddosProtectionPlanResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "peerings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/peeringType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Virtual Network Peering configurations."
+ }
+ },
+ "vnetEncryption": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "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."
+ }
+ },
+ "vnetEncryptionEnforcement": {
+ "type": "string",
+ "defaultValue": "AllowUnencrypted",
+ "allowedValues": [
+ "AllowUnencrypted",
+ "DropUnencrypted"
+ ],
+ "metadata": {
+ "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled."
+ }
+ },
+ "flowTimeoutInMinutes": {
+ "type": "int",
+ "defaultValue": 0,
+ "maxValue": 30,
+ "metadata": {
+ "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."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "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 resource."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "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": {
+ "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,
+ "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-virtualnetwork.{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"
+ }
+ }
+ }
+ }
+ },
+ "virtualNetwork": {
+ "type": "Microsoft.Network/virtualNetworks",
+ "apiVersion": "2024-05-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "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')]"
+ }
+ },
+ "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.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": [
+ "virtualNetwork"
+ ]
+ },
+ "virtualNetwork_diagnosticSettings": {
+ "copy": {
+ "name": "virtualNetwork_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.Network/virtualNetworks/{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": [
+ "virtualNetwork"
+ ]
+ },
+ "virtualNetwork_roleAssignments": {
+ "copy": {
+ "name": "virtualNetwork_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "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]",
+ "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": [
+ "virtualNetwork"
+ ]
+ },
+ "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}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualNetworkName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]"
+ },
+ "addressPrefix": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]"
+ },
+ "addressPrefixes": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]"
+ },
+ "ipamPoolPrefixAllocations": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'ipamPoolPrefixAllocations')]"
+ },
+ "applicationGatewayIPConfigurations": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]"
+ },
+ "delegation": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]"
+ },
+ "natGatewayResourceId": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]"
+ },
+ "networkSecurityGroupResourceId": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]"
+ },
+ "privateEndpointNetworkPolicies": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]"
+ },
+ "privateLinkServiceNetworkPolicies": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]"
+ },
+ "routeTableResourceId": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]"
+ },
+ "serviceEndpointPolicies": {
+ "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]"
+ },
+ "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": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "9728353654559466189"
+ },
+ "name": "Virtual Network Subnets",
+ "description": "This module deploys a Virtual Network Subnet."
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Name of the subnet resource."
+ }
+ },
+ "virtualNetworkName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment."
+ }
+ },
+ "addressPrefix": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty."
+ }
+ },
+ "ipamPoolPrefixAllocations": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty."
+ }
+ },
+ "networkSecurityGroupResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the network security group to assign to the subnet."
+ }
+ },
+ "routeTableResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the route table to assign to the subnet."
+ }
+ },
+ "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 delegation to enable on the subnet."
+ }
+ },
+ "natGatewayResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "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-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.Network/virtualNetworks",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('virtualNetworkName')]"
+ },
+ "subnet": {
+ "type": "Microsoft.Network/virtualNetworks/subnets",
+ "apiVersion": "2024-05-01",
+ "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]",
+ "properties": {
+ "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": "subnet_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "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]",
+ "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": [
+ "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 virtual network peering."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the virtual network peering."
+ },
+ "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]"
+ },
+ "addressPrefix": {
+ "type": "string",
+ "metadata": {
+ "description": "The address prefix for the subnet."
+ },
+ "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]"
+ },
+ "addressPrefixes": {
+ "type": "array",
+ "metadata": {
+ "description": "List of address prefixes for the subnet."
+ },
+ "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]"
+ },
+ "ipamPoolPrefixAllocations": {
+ "type": "array",
+ "metadata": {
+ "description": "The IPAM pool prefix allocations for the subnet."
+ },
+ "value": "[coalesce(tryGet(reference('subnet'), 'ipamPoolPrefixAllocations'), createArray())]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "virtualNetwork"
+ ]
+ },
+ "virtualNetwork_peering_local": {
+ "copy": {
+ "name": "virtualNetwork_peering_local",
+ "count": "[length(coalesce(parameters('peerings'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "localVnetName": {
+ "value": "[parameters('name')]"
+ },
+ "remoteVirtualNetworkResourceId": {
+ "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]"
+ },
+ "name": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]"
+ },
+ "allowForwardedTraffic": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]"
+ },
+ "allowGatewayTransit": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]"
+ },
+ "allowVirtualNetworkAccess": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]"
+ },
+ "doNotVerifyRemoteGateways": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]"
+ },
+ "useRemoteGateways": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "11179987886456111827"
+ },
+ "name": "Virtual Network Peerings",
+ "description": "This module deploys a Virtual Network Peering."
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]",
+ "metadata": {
+ "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName."
+ }
+ },
+ "localVnetName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment."
+ }
+ },
+ "remoteVirtualNetworkResourceId": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "allowForwardedTraffic": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "allowGatewayTransit": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false."
+ }
+ },
+ "allowVirtualNetworkAccess": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "doNotVerifyRemoteGateways": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true."
+ }
+ },
+ "useRemoteGateways": {
+ "type": "bool",
+ "defaultValue": false,
+ "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."
+ }
+ }
+ },
+ "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')]"
+ }
+ }
+ }
+ ],
+ "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 virtual network peering."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the virtual network peering."
+ },
+ "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "virtualNetwork",
+ "virtualNetwork_subnets"
+ ]
+ },
+ "virtualNetwork_peering_remote": {
+ "copy": {
+ "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}-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": {
+ "localVnetName": {
+ "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]"
+ },
+ "remoteVirtualNetworkResourceId": {
+ "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]"
+ },
+ "name": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]"
+ },
+ "allowForwardedTraffic": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]"
+ },
+ "allowGatewayTransit": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]"
+ },
+ "allowVirtualNetworkAccess": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]"
+ },
+ "doNotVerifyRemoteGateways": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]"
+ },
+ "useRemoteGateways": {
+ "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "11179987886456111827"
+ },
+ "name": "Virtual Network Peerings",
+ "description": "This module deploys a Virtual Network Peering."
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]",
+ "metadata": {
+ "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName."
+ }
+ },
+ "localVnetName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment."
+ }
+ },
+ "remoteVirtualNetworkResourceId": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "allowForwardedTraffic": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "allowGatewayTransit": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false."
+ }
+ },
+ "allowVirtualNetworkAccess": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "doNotVerifyRemoteGateways": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true."
+ }
+ },
+ "useRemoteGateways": {
+ "type": "bool",
+ "defaultValue": false,
+ "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."
+ }
+ }
+ },
+ "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')]"
+ }
+ }
+ }
+ ],
+ "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 virtual network peering."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the virtual network peering."
+ },
+ "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": [
+ "nsgs"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "value": "[reference('virtualNetwork').outputs.name.value]"
+ },
+ "resourceId": {
+ "type": "string",
+ "value": "[reference('virtualNetwork').outputs.resourceId.value]"
+ },
+ "subnets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/subnetOutputType"
+ },
+ "copy": {
+ "count": "[length(parameters('subnets'))]",
+ "input": {
+ "name": "[parameters('subnets')[copyIndex()].name]",
+ "resourceId": "[reference('virtualNetwork').outputs.subnetResourceIds.value[copyIndex()]]",
+ "nsgName": "[if(not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup'))), tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name'), null())]",
+ "nsgResourceId": "[if(not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup'))), reference(format('nsgs[{0}]', copyIndex())).outputs.resourceId.value, null())]"
+ }
+ }
+ },
+ "backendSubnetResourceId": {
+ "type": "string",
+ "value": "[if(contains(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'backend'), reference('virtualNetwork').outputs.subnetResourceIds.value[indexOf(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'backend')], '')]"
+ },
+ "containerSubnetResourceId": {
+ "type": "string",
+ "value": "[if(contains(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'containers'), reference('virtualNetwork').outputs.subnetResourceIds.value[indexOf(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'containers')], '')]"
+ },
+ "administrationSubnetResourceId": {
+ "type": "string",
+ "value": "[if(contains(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'administration'), reference('virtualNetwork').outputs.subnetResourceIds.value[indexOf(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'administration')], '')]"
+ },
+ "webserverfarmSubnetResourceId": {
+ "type": "string",
+ "value": "[if(contains(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'webserverfarm'), reference('virtualNetwork').outputs.subnetResourceIds.value[indexOf(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'webserverfarm')], '')]"
+ },
+ "bastionSubnetResourceId": {
+ "type": "string",
+ "value": "[if(contains(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'AzureBastionSubnet'), reference('virtualNetwork').outputs.subnetResourceIds.value[indexOf(map(parameters('subnets'), lambda('subnet', lambdaVariables('subnet').name)), 'AzureBastionSubnet')], '')]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "bastionHost": {
+ "condition": "[parameters('enablePrivateNetworking')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.network.bastion-host.{0}', variables('bastionResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('bastionResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "skuName": {
+ "value": "Standard"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "virtualNetworkResourceId": {
+ "value": "[tryGet(tryGet(tryGet(if(parameters('enablePrivateNetworking'), reference('virtualNetwork'), null()), 'outputs'), 'resourceId'), 'value')]"
+ },
+ "availabilityZones": {
+ "value": []
+ },
+ "publicIPAddressObject": {
+ "value": {
+ "name": "[format('pip-bas{0}', variables('solutionSuffix'))]",
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value))), null())]",
+ "tags": "[parameters('tags')]"
+ }
+ },
+ "disableCopyPaste": {
+ "value": true
+ },
+ "enableFileCopy": {
+ "value": false
+ },
+ "enableIpConnect": {
+ "value": false
+ },
+ "enableShareableLink": {
+ "value": false
+ },
+ "scaleUnits": {
+ "value": 4
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', 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.36.177.2456",
+ "templateHash": "13495809792504478069"
+ },
+ "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."
+ }
+ },
+ "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."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a diagnostic setting. To be used if only 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."
+ }
+ }
+ },
+ "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 Azure Bastion resource."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "virtualNetworkResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Shared services Virtual Network resource Id."
+ }
+ },
+ "bastionSubnetPublicIpResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "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'))]"
+ },
+ "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."
+ }
+ },
+ "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."
+ }
+ },
+ "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/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."
+ }
+ },
+ "availabilityZones": {
+ "type": "array",
+ "items": {
+ "type": "int"
+ },
+ "defaultValue": [
+ 1,
+ 2,
+ 3
+ ],
+ "allowedValues": [
+ 1,
+ 2,
+ 3
+ ],
+ "metadata": {
+ "description": "Optional. The list of Availability zones to use for the zone-redundant 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)))))]"
+ }
+ ],
+ "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')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.network-bastionhost.{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"
+ }
+ }
+ }
+ }
+ },
+ "azureBastion": {
+ "type": "Microsoft.Network/bastionHosts",
+ "apiVersion": "2024-05-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[coalesce(parameters('tags'), createObject())]",
+ "sku": {
+ "name": "[parameters('skuName')]"
+ },
+ "zones": "[if(equals(parameters('skuName'), 'Developer'), createArray(), map(parameters('availabilityZones'), lambda('zone', format('{0}', 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"
+ ]
+ },
+ "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": {
+ "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')]"
+ },
+ "dependsOn": [
+ "azureBastion"
+ ]
+ },
+ "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": {
+ "value": "[parameters('publicIPAddressObject').name]"
+ },
+ "enableTelemetry": {
+ "value": "[variables('enableReferencedModulesTelemetry')]"
+ },
+ "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": {
+ "value": "[tryGet(parameters('publicIPAddressObject'), 'roleAssignments')]"
+ },
+ "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(not(empty(parameters('availabilityZones'))), parameters('availabilityZones'), 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": {
+ "domainNameLabel": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "domainNameLabelScope": {
+ "type": "string",
+ "allowedValues": [
+ "NoReuse",
+ "ResourceGroupReuse",
+ "SubscriptionReuse",
+ "TenantReuse"
+ ],
+ "nullable": true,
+ "metadata": {
+ "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
+ }
+ },
+ "ddosSettingsType": {
+ "type": "object",
+ "properties": {
+ "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": "Optional. The DDoS protection plan associated with the public IP address."
+ }
+ },
+ "protectionMode": {
+ "type": "string",
+ "allowedValues": [
+ "Enabled"
+ ],
+ "metadata": {
+ "description": "Required. The DDoS protection policy customizations."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true
+ }
+ },
+ "ipTagType": {
+ "type": "object",
+ "properties": {
+ "ipTagType": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The IP tag type."
+ }
+ },
+ "tag": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The IP tag."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true
+ }
+ },
+ "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.2.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.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\"."
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the Public IP Address."
+ }
+ },
+ "publicIpPrefixResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "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 DNS settings of the public IP address."
+ }
+ },
+ "ipTags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ipTagType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The list of tags associated with the public IP address."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "defaultValue": "Standard",
+ "allowedValues": [
+ "Basic",
+ "Standard"
+ ],
+ "metadata": {
+ "description": "Optional. Name of a public IP address SKU."
+ }
+ },
+ "skuTier": {
+ "type": "string",
+ "defaultValue": "Regional",
+ "allowedValues": [
+ "Global",
+ "Regional"
+ ],
+ "metadata": {
+ "description": "Optional. Tier of a public IP address SKU."
+ }
+ },
+ "ddosSettings": {
+ "$ref": "#/definitions/ddosSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DDoS protection plan configuration associated with the public IP address."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "idleTimeoutInMinutes": {
+ "type": "int",
+ "defaultValue": 4,
+ "metadata": {
+ "description": "Optional. The idle timeout of the public IP address."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "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')]",
+ "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-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"
+ }
+ }
+ }
+ }
+ },
+ "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": {
+ "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')]"
+ }
+ },
+ "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": "publicIpAddress_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "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]",
+ "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": [
+ "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 public IP address."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the public IP address."
+ },
+ "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]"
+ },
+ "ipAddress": {
+ "type": "string",
+ "metadata": {
+ "description": "The public IP address of the public IP address resource."
+ },
+ "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."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name the Azure Bastion."
+ },
+ "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": [
+ "logAnalyticsWorkspace",
+ "virtualNetwork"
+ ]
+ },
+ "maintenanceConfiguration": {
+ "condition": "[parameters('enablePrivateNetworking')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.compute.virtual-machine.{0}', variables('maintenanceConfigurationResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('maintenanceConfigurationResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "extensionProperties": {
+ "value": {
+ "InGuestPatchMode": "User"
+ }
+ },
+ "maintenanceScope": {
+ "value": "InGuestPatch"
+ },
+ "maintenanceWindow": {
+ "value": {
+ "startDateTime": "2024-06-16 00:00",
+ "duration": "03:55",
+ "timeZone": "W. Europe Standard Time",
+ "recurEvery": "1Day"
+ }
+ },
+ "visibility": {
+ "value": "Custom"
+ },
+ "installPatches": {
+ "value": {
+ "rebootSetting": "IfRequired",
+ "windowsParameters": {
+ "classificationsToInclude": [
+ "Critical",
+ "Security"
+ ]
+ },
+ "linuxParameters": {
+ "classificationsToInclude": [
+ "Critical",
+ "Security"
+ ]
+ }
+ }
+ }
+ },
+ "template": {
+ "$schema": "https://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": "16060601297152129929"
+ },
+ "name": "Maintenance Configurations",
+ "description": "This module deploys a Maintenance Configuration."
+ },
+ "definitions": {
+ "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. Maintenance Configuration Name."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "extensionProperties": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Gets or sets extensionProperties of the maintenanceConfiguration."
+ }
+ },
+ "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."
+ }
+ },
+ "maintenanceScope": {
+ "type": "string",
+ "defaultValue": "Host",
+ "allowedValues": [
+ "Host",
+ "OSImage",
+ "Extension",
+ "InGuestPatch",
+ "SQLDB",
+ "SQLManagedInstance"
+ ],
+ "metadata": {
+ "description": "Optional. Gets or sets maintenanceScope of the configuration."
+ }
+ },
+ "maintenanceWindow": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Definition of a MaintenanceWindow."
+ }
+ },
+ "namespace": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Gets or sets namespace of the resource."
+ }
+ },
+ "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. Gets or sets tags of the resource."
+ }
+ },
+ "visibility": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "",
+ "Custom",
+ "Public"
+ ],
+ "metadata": {
+ "description": "Optional. Gets or sets the visibility of the configuration. The default value is 'Custom'."
+ }
+ },
+ "installPatches": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Configuration settings for VM guest patching with Azure Update Manager."
+ }
+ }
+ },
+ "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')]",
+ "Scheduled Patching Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cd08ab90-6b14-449c-ad9a-8f8e549482c6')]",
+ "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.maintenance-maintenanceconfiguration.{0}.{1}', replace('0.3.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"
+ }
+ }
+ }
+ }
+ },
+ "maintenanceConfiguration": {
+ "type": "Microsoft.Maintenance/maintenanceConfigurations",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "extensionProperties": "[parameters('extensionProperties')]",
+ "maintenanceScope": "[parameters('maintenanceScope')]",
+ "maintenanceWindow": "[parameters('maintenanceWindow')]",
+ "namespace": "[parameters('namespace')]",
+ "visibility": "[parameters('visibility')]",
+ "installPatches": "[if(equals(parameters('maintenanceScope'), 'InGuestPatch'), parameters('installPatches'), null())]"
+ }
+ },
+ "maintenanceConfiguration_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.Maintenance/maintenanceConfigurations/{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": [
+ "maintenanceConfiguration"
+ ]
+ },
+ "maintenanceConfiguration_roleAssignments": {
+ "copy": {
+ "name": "maintenanceConfiguration_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Maintenance/maintenanceConfigurations/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Maintenance/maintenanceConfigurations', 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": [
+ "maintenanceConfiguration"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Maintenance Configuration."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Maintenance Configuration."
+ },
+ "value": "[resourceId('Microsoft.Maintenance/maintenanceConfigurations', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the Maintenance Configuration was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the Maintenance Configuration was created in."
+ },
+ "value": "[reference('maintenanceConfiguration', '2023-04-01', 'full').location]"
+ }
+ }
+ }
+ }
+ },
+ "windowsVmDataCollectionRules": {
+ "condition": "[and(parameters('enablePrivateNetworking'), parameters('enableMonitoring'))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.insights.data-collection-rule.{0}', variables('dataCollectionRulesResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('dataCollectionRulesResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "location": "[if(variables('useExistingLogAnalytics'), createObject('value', reference('existingLogAnalyticsWorkspace', '2020-08-01', 'full').location), createObject('value', reference('logAnalyticsWorkspace').outputs.location.value))]",
+ "dataCollectionRuleProperties": {
+ "value": {
+ "kind": "Windows",
+ "dataSources": {
+ "performanceCounters": [
+ {
+ "streams": [
+ "Microsoft-Perf"
+ ],
+ "samplingFrequencyInSeconds": 60,
+ "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"
+ }
+ ],
+ "windowsEventLogs": [
+ {
+ "name": "SecurityAuditEvents",
+ "streams": [
+ "Microsoft-WindowsEvent"
+ ],
+ "eventLogName": "Security",
+ "eventTypes": [
+ {
+ "eventType": "Audit Success"
+ },
+ {
+ "eventType": "Audit Failure"
+ }
+ ],
+ "xPathQueries": [
+ "Security!*[System[(EventID=4624 or EventID=4625)]]"
+ ]
+ }
+ ]
+ },
+ "destinations": {
+ "logAnalytics": [
+ {
+ "workspaceResourceId": "[if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)]",
+ "name": "la--1264800308"
+ }
+ ]
+ },
+ "dataFlows": [
+ {
+ "streams": [
+ "Microsoft-Perf"
+ ],
+ "destinations": [
+ "la--1264800308"
+ ],
+ "transformKql": "source",
+ "outputStream": "Microsoft-Perf"
+ }
+ ]
+ }
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "2876269346663744311"
+ },
+ "name": "Data Collection Rules",
+ "description": "This module deploys a Data Collection Rule."
+ },
+ "definitions": {
+ "dataCollectionRulePropertiesType": {
+ "type": "object",
+ "discriminator": {
+ "propertyName": "kind",
+ "mapping": {
+ "Linux": {
+ "$ref": "#/definitions/linuxDcrPropertiesType"
+ },
+ "Windows": {
+ "$ref": "#/definitions/windowsDcrPropertiesType"
+ },
+ "All": {
+ "$ref": "#/definitions/allPlatformsDcrPropertiesType"
+ },
+ "AgentSettings": {
+ "$ref": "#/definitions/agentSettingsDcrPropertiesType"
+ },
+ "Direct": {
+ "$ref": "#/definitions/directDcrPropertiesType"
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for data collection rule properties. Depending on the kind, the properties will be different."
+ }
+ },
+ "linuxDcrPropertiesType": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "Linux"
+ ],
+ "metadata": {
+ "description": "Required. The platform type specifies the type of resources this rule can apply to."
+ }
+ },
+ "dataSources": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of data sources that will be collected."
+ }
+ },
+ "dataFlows": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. The specification of data flows."
+ }
+ },
+ "destinations": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of destinations that can be used in data flows."
+ }
+ },
+ "dataCollectionEndpointResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the data collection endpoint that this rule can be used with."
+ }
+ },
+ "streamDeclarations": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Declaration of custom streams used in this rule."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Description of the data collection rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the properties of the 'Linux' data collection rule."
+ }
+ },
+ "windowsDcrPropertiesType": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "Windows"
+ ],
+ "metadata": {
+ "description": "Required. The platform type specifies the type of resources this rule can apply to."
+ }
+ },
+ "dataSources": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of data sources that will be collected."
+ }
+ },
+ "dataFlows": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. The specification of data flows."
+ }
+ },
+ "destinations": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of destinations that can be used in data flows."
+ }
+ },
+ "dataCollectionEndpointResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the data collection endpoint that this rule can be used with."
+ }
+ },
+ "streamDeclarations": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Declaration of custom streams used in this rule."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Description of the data collection rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the properties of the 'Windows' data collection rule."
+ }
+ },
+ "allPlatformsDcrPropertiesType": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "All"
+ ],
+ "metadata": {
+ "description": "Required. The platform type specifies the type of resources this rule can apply to."
+ }
+ },
+ "dataSources": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of data sources that will be collected."
+ }
+ },
+ "dataFlows": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. The specification of data flows."
+ }
+ },
+ "destinations": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of destinations that can be used in data flows."
+ }
+ },
+ "dataCollectionEndpointResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the data collection endpoint that this rule can be used with."
+ }
+ },
+ "streamDeclarations": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Declaration of custom streams used in this rule."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Description of the data collection rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the properties of the data collection rule of the kind 'All'."
+ }
+ },
+ "agentSettingsDcrPropertiesType": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "AgentSettings"
+ ],
+ "metadata": {
+ "description": "Required. The platform type specifies the type of resources this rule can apply to."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Description of the data collection rule."
+ }
+ },
+ "agentSettings": {
+ "$ref": "#/definitions/agentSettingsType",
+ "metadata": {
+ "description": "Required. Agent settings used to modify agent behavior on a given host."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the properties of the 'AgentSettings' data collection rule."
+ }
+ },
+ "agentSettingsType": {
+ "type": "object",
+ "properties": {
+ "logs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/agentSettingType"
+ },
+ "metadata": {
+ "description": "Required. All the settings that are applicable to the logs agent (AMA)."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the agent settings."
+ }
+ },
+ "agentSettingType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "allowedValues": [
+ "MaxDiskQuotaInMB",
+ "UseTimeReceivedForForwardedEvents"
+ ],
+ "metadata": {
+ "description": "Required. The name of the agent setting."
+ }
+ },
+ "value": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The value of the agent setting."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the (single) agent setting."
+ }
+ },
+ "directDcrPropertiesType": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "Direct"
+ ],
+ "metadata": {
+ "description": "Required. The platform type specifies the type of resources this rule can apply to."
+ }
+ },
+ "dataFlows": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. The specification of data flows."
+ }
+ },
+ "destinations": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Specification of destinations that can be used in data flows."
+ }
+ },
+ "dataCollectionEndpointResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the data collection endpoint that this rule can be used with."
+ }
+ },
+ "streamDeclarations": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Declaration of custom streams used in this rule."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Description of the data collection rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the properties of the 'Direct' data collection rule."
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "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 data collection rule. The name is case insensitive."
+ }
+ },
+ "dataCollectionRuleProperties": {
+ "$ref": "#/definitions/dataCollectionRulePropertiesType",
+ "metadata": {
+ "description": "Required. The kind of data collection rule."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "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."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "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. Resource tags."
+ }
+ }
+ },
+ "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', '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')]",
+ "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')]"
+ },
+ "dataCollectionRulePropertiesUnion": "[union(createObject('description', tryGet(parameters('dataCollectionRuleProperties'), 'description')), if(or(or(equals(parameters('dataCollectionRuleProperties').kind, 'Linux'), equals(parameters('dataCollectionRuleProperties').kind, 'Windows')), equals(parameters('dataCollectionRuleProperties').kind, 'All')), createObject('dataSources', parameters('dataCollectionRuleProperties').dataSources), createObject()), if(or(or(or(equals(parameters('dataCollectionRuleProperties').kind, 'Linux'), equals(parameters('dataCollectionRuleProperties').kind, 'Windows')), equals(parameters('dataCollectionRuleProperties').kind, 'All')), equals(parameters('dataCollectionRuleProperties').kind, 'Direct')), createObject('dataFlows', parameters('dataCollectionRuleProperties').dataFlows, 'destinations', parameters('dataCollectionRuleProperties').destinations, 'dataCollectionEndpointId', tryGet(parameters('dataCollectionRuleProperties'), 'dataCollectionEndpointResourceId'), 'streamDeclarations', tryGet(parameters('dataCollectionRuleProperties'), 'streamDeclarations')), createObject()), if(equals(parameters('dataCollectionRuleProperties').kind, 'AgentSettings'), createObject('agentSettings', parameters('dataCollectionRuleProperties').agentSettings), createObject()))]"
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.insights-datacollectionrule.{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"
+ }
+ }
+ }
+ }
+ },
+ "dataCollectionRule": {
+ "condition": "[not(equals(parameters('dataCollectionRuleProperties').kind, 'All'))]",
+ "type": "Microsoft.Insights/dataCollectionRules",
+ "apiVersion": "2023-03-11",
+ "name": "[parameters('name')]",
+ "kind": "[parameters('dataCollectionRuleProperties').kind]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
+ "properties": "[variables('dataCollectionRulePropertiesUnion')]"
+ },
+ "dataCollectionRuleAll": {
+ "condition": "[equals(parameters('dataCollectionRuleProperties').kind, 'All')]",
+ "type": "Microsoft.Insights/dataCollectionRules",
+ "apiVersion": "2023-03-11",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
+ "properties": "[variables('dataCollectionRulePropertiesUnion')]"
+ },
+ "dataCollectionRule_conditionalScopeResources": {
+ "condition": "[or(and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None'))), not(empty(coalesce(parameters('roleAssignments'), createArray()))))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-DCR-ConditionalScope', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "dataCollectionRuleName": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), createObject('value', parameters('name')), createObject('value', parameters('name')))]",
+ "builtInRoleNames": {
+ "value": "[variables('builtInRoleNames')]"
+ },
+ "lock": {
+ "value": "[parameters('lock')]"
+ },
+ "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.36.177.2456",
+ "templateHash": "17062698556609624183"
+ }
+ },
+ "definitions": {
+ "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": {
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "builtInRoleNames": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Built-in role names."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "dataCollectionRuleName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Data Collection Rule to assign the role(s) to."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "formattedRoleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]",
+ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(parameters('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)))))]"
+ }
+ ]
+ },
+ "resources": {
+ "dataCollectionRule": {
+ "existing": true,
+ "type": "Microsoft.Insights/dataCollectionRules",
+ "apiVersion": "2023-03-11",
+ "name": "[parameters('dataCollectionRuleName')]"
+ },
+ "dataCollectionRule_roleAssignments": {
+ "copy": {
+ "name": "dataCollectionRule_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Insights/dataCollectionRules/{0}', parameters('dataCollectionRuleName'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceGroup().id, 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')]"
+ }
+ },
+ "dataCollectionRule_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.Insights/dataCollectionRules/{0}', parameters('dataCollectionRuleName'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('dataCollectionRuleName')))]",
+ "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": [
+ "dataCollectionRule",
+ "dataCollectionRuleAll"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the dataCollectionRule."
+ },
+ "value": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), parameters('name'), parameters('name'))]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the dataCollectionRule."
+ },
+ "value": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), resourceId('Microsoft.Insights/dataCollectionRules', parameters('name')), resourceId('Microsoft.Insights/dataCollectionRules', parameters('name')))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the dataCollectionRule was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), reference('dataCollectionRuleAll', '2023-03-11', 'full').location, reference('dataCollectionRule', '2023-03-11', 'full').location)]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), tryGet(tryGet(if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), reference('dataCollectionRuleAll', '2023-03-11', 'full'), null()), 'identity'), 'principalId'), tryGet(tryGet(if(not(equals(parameters('dataCollectionRuleProperties').kind, 'All')), reference('dataCollectionRule', '2023-03-11', 'full'), null()), 'identity'), 'principalId'))]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "existingLogAnalyticsWorkspace",
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "proximityPlacementGroup": {
+ "condition": "[parameters('enablePrivateNetworking')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.compute.proximity-placement-group.{0}', variables('proximityPlacementGroupResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('proximityPlacementGroupResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "availabilityZone": {
+ "value": "[variables('virtualMachineAvailabilityZone')]"
+ },
+ "intent": {
+ "value": {
+ "vmSizes": [
+ "[parameters('virtualMachineSize')]"
+ ]
+ }
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "710462462329438227"
+ },
+ "name": "Proximity Placement Groups",
+ "description": "This module deploys a Proximity Placement Group."
+ },
+ "definitions": {
+ "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. The name of the proximity placement group that is being created."
+ }
+ },
+ "type": {
+ "type": "string",
+ "defaultValue": "Standard",
+ "allowedValues": [
+ "Standard",
+ "Ultra"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the type of the proximity placement group."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Resource location."
+ }
+ },
+ "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 proximity placement group resource."
+ }
+ },
+ "availabilityZone": {
+ "type": "int",
+ "allowedValues": [
+ -1,
+ 1,
+ 2,
+ 3
+ ],
+ "metadata": {
+ "description": "Required. Specifies the Availability Zone where virtual machine, virtual machine scale set or availability set associated with the proximity placement group can be created. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If set to -1, no zone is defined. Note that the availability zone numbers here are the logical availability zone in your Azure subscription. Different subscriptions might have a different mapping of the physical zone and logical zone. To understand more, please refer to [Physical and logical availability zones](https://learn.microsoft.com/en-us/azure/reliability/availability-zones-overview?tabs=azure-cli#physical-and-logical-availability-zones)."
+ }
+ },
+ "colocationStatus": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Describes colocation status of the Proximity Placement Group."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "intent": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the user intent of the proximity placement group."
+ }
+ }
+ },
+ "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')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.compute-proximityplacementgroup.{0}.{1}', replace('0.4.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"
+ }
+ }
+ }
+ }
+ },
+ "proximityPlacementGroup": {
+ "type": "Microsoft.Compute/proximityPlacementGroups",
+ "apiVersion": "2022-08-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "zones": "[if(not(equals(parameters('availabilityZone'), -1)), array(string(parameters('availabilityZone'))), null())]",
+ "properties": {
+ "proximityPlacementGroupType": "[parameters('type')]",
+ "colocationStatus": "[parameters('colocationStatus')]",
+ "intent": "[parameters('intent')]"
+ }
+ },
+ "proximityPlacementGroup_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.Compute/proximityPlacementGroups/{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": [
+ "proximityPlacementGroup"
+ ]
+ },
+ "proximityPlacementGroup_roleAssignments": {
+ "copy": {
+ "name": "proximityPlacementGroup_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Compute/proximityPlacementGroups/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/proximityPlacementGroups', 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": [
+ "proximityPlacementGroup"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the proximity placement group."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resourceId the proximity placement group."
+ },
+ "value": "[resourceId('Microsoft.Compute/proximityPlacementGroups', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the proximity placement group was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('proximityPlacementGroup', '2022-08-01', 'full').location]"
+ }
+ }
+ }
+ }
+ },
+ "virtualMachine": {
+ "condition": "[parameters('enablePrivateNetworking')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.compute.virtual-machine.{0}', variables('virtualMachineResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('virtualMachineResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "computerName": {
+ "value": "[take(variables('virtualMachineResourceName'), 15)]"
+ },
+ "osType": {
+ "value": "Windows"
+ },
+ "vmSize": {
+ "value": "[parameters('virtualMachineSize')]"
+ },
+ "adminUsername": {
+ "value": "[coalesce(parameters('virtualMachineAdminUsername'), 'JumpboxAdminUser')]"
+ },
+ "adminPassword": {
+ "value": "[coalesce(parameters('virtualMachineAdminPassword'), 'JumpboxAdminP@ssw0rd1234!')]"
+ },
+ "patchMode": {
+ "value": "AutomaticByPlatform"
+ },
+ "bypassPlatformSafetyChecksOnUserSchedule": {
+ "value": true
+ },
+ "maintenanceConfigurationResourceId": {
+ "value": "[reference('maintenanceConfiguration').outputs.resourceId.value]"
+ },
+ "enableAutomaticUpdates": {
+ "value": true
+ },
+ "encryptionAtHost": {
+ "value": true
+ },
+ "availabilityZone": {
+ "value": "[variables('virtualMachineAvailabilityZone')]"
+ },
+ "proximityPlacementGroupResourceId": {
+ "value": "[reference('proximityPlacementGroup').outputs.resourceId.value]"
+ },
+ "imageReference": {
+ "value": {
+ "publisher": "microsoft-dsvm",
+ "offer": "dsvm-win-2022",
+ "sku": "winserver-2022",
+ "version": "latest"
+ }
+ },
+ "osDisk": {
+ "value": {
+ "name": "[format('osdisk-{0}', variables('virtualMachineResourceName'))]",
+ "caching": "ReadWrite",
+ "createOption": "FromImage",
+ "deleteOption": "Delete",
+ "diskSizeGB": 128,
+ "managedDisk": {
+ "storageAccountType": "Premium_LRS"
+ }
+ }
+ },
+ "nicConfigurations": {
+ "value": [
+ {
+ "name": "[format('nic-{0}', variables('virtualMachineResourceName'))]",
+ "tags": "[parameters('tags')]",
+ "deleteOption": "Delete",
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value))), null())]",
+ "ipConfigurations": [
+ {
+ "name": "[format('{0}-nic01-ipconfig01', variables('virtualMachineResourceName'))]",
+ "subnetResourceId": "[reference('virtualNetwork').outputs.administrationSubnetResourceId.value]",
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value))), null())]"
+ }
+ ]
+ }
+ ]
+ },
+ "extensionAadJoinConfig": {
+ "value": {
+ "enabled": true,
+ "tags": "[parameters('tags')]",
+ "typeHandlerVersion": "1.0"
+ }
+ },
+ "extensionAntiMalwareConfig": {
+ "value": {
+ "enabled": true,
+ "settings": {
+ "AntimalwareEnabled": "true",
+ "Exclusions": {},
+ "RealtimeProtectionEnabled": "true",
+ "ScheduledScanSettings": {
+ "day": "7",
+ "isEnabled": "true",
+ "scanType": "Quick",
+ "time": "120"
+ }
+ },
+ "tags": "[parameters('tags')]"
+ }
+ },
+ "extensionMonitoringAgentConfig": "[if(parameters('enableMonitoring'), createObject('value', createObject('dataCollectionRuleAssociations', createArray(createObject('dataCollectionRuleResourceId', reference('windowsVmDataCollectionRules').outputs.resourceId.value, 'name', format('send-{0}', if(variables('useExistingLogAnalytics'), variables('existingLawName'), reference('logAnalyticsWorkspace').outputs.name.value)))), 'enabled', true(), 'tags', parameters('tags'))), createObject('value', null()))]",
+ "extensionNetworkWatcherAgentConfig": {
+ "value": {
+ "enabled": true,
+ "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.36.177.2456",
+ "templateHash": "4862190245661245221"
+ },
+ "name": "Virtual Machines",
+ "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs."
+ },
+ "definitions": {
+ "osDiskType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The disk name."
+ }
+ },
+ "diskSizeGB": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the size of an empty data disk in gigabytes."
+ }
+ },
+ "createOption": {
+ "type": "string",
+ "allowedValues": [
+ "Attach",
+ "Empty",
+ "FromImage"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies how the virtual machine should be created."
+ }
+ },
+ "deleteOption": {
+ "type": "string",
+ "allowedValues": [
+ "Delete",
+ "Detach"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies whether data disk should be deleted or detached upon VM deletion."
+ }
+ },
+ "caching": {
+ "type": "string",
+ "allowedValues": [
+ "None",
+ "ReadOnly",
+ "ReadWrite"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the caching requirements."
+ }
+ },
+ "diffDiskSettings": {
+ "type": "object",
+ "properties": {
+ "placement": {
+ "type": "string",
+ "allowedValues": [
+ "CacheDisk",
+ "NvmeDisk",
+ "ResourceDisk"
+ ],
+ "metadata": {
+ "description": "Required. Specifies the ephemeral disk placement for the operating system disk."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the ephemeral Disk Settings for the operating system disk."
+ }
+ },
+ "managedDisk": {
+ "type": "object",
+ "properties": {
+ "storageAccountType": {
+ "type": "string",
+ "allowedValues": [
+ "PremiumV2_LRS",
+ "Premium_LRS",
+ "Premium_ZRS",
+ "StandardSSD_LRS",
+ "StandardSSD_ZRS",
+ "Standard_LRS",
+ "UltraSSD_LRS"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the storage account type for the managed disk."
+ }
+ },
+ "diskEncryptionSetResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the customer managed disk encryption set resource id for the managed disk."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. The managed disk parameters."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing an OS disk."
+ }
+ },
+ "dataDiskType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The disk name. When attaching a pre-existing disk, this name is ignored and the name of the existing disk is used."
+ }
+ },
+ "lun": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the logical unit number of the data disk."
+ }
+ },
+ "diskSizeGB": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the size of an empty data disk in gigabytes. This property is ignored when attaching a pre-existing disk."
+ }
+ },
+ "createOption": {
+ "type": "string",
+ "allowedValues": [
+ "Attach",
+ "Empty",
+ "FromImage"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies how the virtual machine should be created. This property is automatically set to 'Attach' when attaching a pre-existing disk."
+ }
+ },
+ "deleteOption": {
+ "type": "string",
+ "allowedValues": [
+ "Delete",
+ "Detach"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies whether data disk should be deleted or detached upon VM deletion. This property is automatically set to 'Detach' when attaching a pre-existing disk."
+ }
+ },
+ "caching": {
+ "type": "string",
+ "allowedValues": [
+ "None",
+ "ReadOnly",
+ "ReadWrite"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the caching requirements. This property is automatically set to 'None' when attaching a pre-existing disk."
+ }
+ },
+ "diskIOPSReadWrite": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The number of IOPS allowed for this disk; only settable for UltraSSD disks. One operation can transfer between 4k and 256k bytes. Ignored when attaching a pre-existing disk."
+ }
+ },
+ "diskMBpsReadWrite": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The bandwidth allowed for this disk; only settable for UltraSSD disks. MBps means millions of bytes per second - MB here uses the ISO notation, of powers of 10. Ignored when attaching a pre-existing disk."
+ }
+ },
+ "managedDisk": {
+ "type": "object",
+ "properties": {
+ "storageAccountType": {
+ "type": "string",
+ "allowedValues": [
+ "PremiumV2_LRS",
+ "Premium_LRS",
+ "Premium_ZRS",
+ "StandardSSD_LRS",
+ "StandardSSD_ZRS",
+ "Standard_LRS",
+ "UltraSSD_LRS"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the storage account type for the managed disk. Ignored when attaching a pre-existing disk."
+ }
+ },
+ "diskEncryptionSetResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the customer managed disk encryption set resource id for the managed disk."
+ }
+ },
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the resource id of a pre-existing managed disk. If the disk should be created, this property should be empty."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. The managed disk parameters."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags of the public IP address. Valid only when creating a new managed disk."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing a data disk."
+ }
+ },
+ "publicKeyType": {
+ "type": "object",
+ "properties": {
+ "keyData": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the SSH public key data used to authenticate through ssh."
+ }
+ },
+ "path": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the full path on the created VM where ssh public key is stored. If the file already exists, the specified key is appended to the file."
+ }
+ }
+ }
+ },
+ "nicConfigurationType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the NIC configuration."
+ }
+ },
+ "nicSuffix": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The suffix to append to the NIC name."
+ }
+ },
+ "enableIPForwarding": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Indicates whether IP forwarding is enabled on this network interface."
+ }
+ },
+ "enableAcceleratedNetworking": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If the network interface is accelerated networking enabled."
+ }
+ },
+ "deleteOption": {
+ "type": "string",
+ "allowedValues": [
+ "Delete",
+ "Detach"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify what happens to the network interface when the VM is deleted."
+ }
+ },
+ "dnsServers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of DNS servers IP addresses. Use 'AzureProvidedDNS' to switch to azure provided DNS resolution. 'AzureProvidedDNS' value cannot be combined with other IPs, it must be the only value in dnsServers collection."
+ }
+ },
+ "networkSecurityGroupResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The network security group (NSG) to attach to the network interface."
+ }
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ipConfigurationType"
+ },
+ "metadata": {
+ "description": "Required. The IP configurations of the network interface."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags of the public IP address."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for the module."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the IP configuration."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the NIC configuration."
+ }
+ },
+ "imageReferenceType": {
+ "type": "object",
+ "properties": {
+ "communityGalleryImageId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specified the community gallery image unique id for vm deployment. This can be fetched from community gallery image GET call."
+ }
+ },
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource Id of the image reference."
+ }
+ },
+ "offer": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the offer of the platform image or marketplace image used to create the virtual machine."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The image publisher."
+ }
+ },
+ "sku": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The SKU of the image."
+ }
+ },
+ "version": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the version of the platform image or marketplace image used to create the virtual machine. The allowed formats are Major.Minor.Build or 'latest'. Even if you use 'latest', the VM image will not automatically update after deploy time even if a new version becomes available."
+ }
+ },
+ "sharedGalleryImageId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specified the shared gallery image unique id for vm deployment. This can be fetched from shared gallery image GET call."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing the image reference."
+ }
+ },
+ "planType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the plan."
+ }
+ },
+ "product": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the product of the image from the marketplace."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The publisher ID."
+ }
+ },
+ "promotionCode": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The promotion code."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Specifies information about the marketplace image used to create the virtual machine."
+ }
+ },
+ "autoShutDownConfigType": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The status of the auto shutdown configuration."
+ }
+ },
+ "timeZone": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The time zone ID (e.g. China Standard Time, Greenland Standard Time, Pacific Standard time, etc.)."
+ }
+ },
+ "dailyRecurrenceTime": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The time of day the schedule will occur."
+ }
+ },
+ "notificationSettings": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The status of the notification settings."
+ }
+ },
+ "emailRecipient": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The email address to send notifications to (can be a list of semi-colon separated email addresses)."
+ }
+ },
+ "notificationLocale": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)."
+ }
+ },
+ "webhookUrl": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The webhook URL to which the notification will be sent."
+ }
+ },
+ "timeInMinutes": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The time in minutes before shutdown to send notifications."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the schedule."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing the configuration profile."
+ }
+ },
+ "vaultSecretGroupType": {
+ "type": "object",
+ "properties": {
+ "sourceVault": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The relative URL of the Key Vault containing all of the certificates in VaultCertificates."
+ }
+ },
+ "vaultCertificates": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "certificateStore": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. For Windows VMs, specifies the certificate store on the Virtual Machine to which the certificate should be added. The specified certificate store is implicitly in the LocalMachine account. For Linux VMs, the certificate file is placed under the /var/lib/waagent directory, with the file name .crt for the X509 certificate file and .prv for private key. Both of these files are .pem formatted."
+ }
+ },
+ "certificateUrl": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. This is the URL of a certificate that has been uploaded to Key Vault as a secret."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The list of key vault references in SourceVault which contain certificates."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing the set of certificates that should be installed onto the virtual machine."
+ }
+ },
+ "vmGalleryApplicationType": {
+ "type": "object",
+ "properties": {
+ "packageReferenceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the GalleryApplicationVersion resource id on the form of /subscriptions/{SubscriptionId}/resourceGroups/{ResourceGroupName}/providers/Microsoft.Compute/galleries/{galleryName}/applications/{application}/versions/{version}."
+ }
+ },
+ "configurationReference": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the uri to an azure blob that will replace the default configuration for the package if provided."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If set to true, when a new Gallery Application version is available in PIR/SIG, it will be automatically updated for the VM/VMSS."
+ }
+ },
+ "order": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the order in which the packages have to be installed."
+ }
+ },
+ "tags": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies a passthrough value for more generic context."
+ }
+ },
+ "treatFailureAsDeploymentFailure": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If true, any failure for any operation in the VmApplication will fail the deployment."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing the gallery application that should be made available to the VM/VMSS."
+ }
+ },
+ "additionalUnattendContentType": {
+ "type": "object",
+ "properties": {
+ "settingName": {
+ "type": "string",
+ "allowedValues": [
+ "AutoLogon",
+ "FirstLogonCommands"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the name of the setting to which the content applies."
+ }
+ },
+ "content": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the XML formatted content that is added to the unattend.xml file for the specified path and component. The XML must be less than 4KB and must include the root element for the setting or feature that is being inserted."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing additional base-64 encoded XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup."
+ }
+ },
+ "winRMListenerType": {
+ "type": "object",
+ "properties": {
+ "certificateUrl": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The URL of a certificate that has been uploaded to Key Vault as a secret."
+ }
+ },
+ "protocol": {
+ "type": "string",
+ "allowedValues": [
+ "Http",
+ "Https"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the protocol of WinRM listener."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing a Windows Remote Management listener."
+ }
+ },
+ "nicConfigurationOutputType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the NIC configuration."
+ }
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/networkInterfaceIPConfigurationOutputType"
+ },
+ "metadata": {
+ "description": "Required. List of IP configurations of the NIC configuration."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type describing the network interface configuration output."
+ }
+ },
+ "_1.applicationGatewayBackendAddressPoolsType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the backend address pool."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the backend address pool that is unique within an Application Gateway."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "backendAddresses": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "ipAddress": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. IP address of the backend address."
+ }
+ },
+ "fqdn": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. FQDN of the backend address."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Backend addresses."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the application gateway backend address pool."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the application gateway backend address pool.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "_1.applicationSecurityGroupType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the application security group."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Location of the application security group."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the application security group."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the application security group."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the application security group.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "_1.backendAddressPoolType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the backend address pool."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the backend address pool."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The properties of the backend address pool."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a backend address pool.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "_1.inboundNatRuleType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the inbound NAT rule."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the resource that is unique within the set of inbound NAT rules used by the load balancer. This name can be used to access the resource."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "backendAddressPool": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A reference to backendAddressPool resource."
+ }
+ },
+ "backendPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port used for the internal endpoint. Acceptable values range from 1 to 65535."
+ }
+ },
+ "enableFloatingIP": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint."
+ }
+ },
+ "enableTcpReset": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP."
+ }
+ },
+ "frontendIPConfiguration": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A reference to frontend IP addresses."
+ }
+ },
+ "frontendPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. Acceptable values range from 1 to 65534."
+ }
+ },
+ "frontendPortRangeStart": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534."
+ }
+ },
+ "frontendPortRangeEnd": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534."
+ }
+ },
+ "protocol": {
+ "type": "string",
+ "allowedValues": [
+ "All",
+ "Tcp",
+ "Udp"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reference to the transport protocol used by the load balancing rule."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the inbound NAT rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the inbound NAT rule.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "_1.virtualNetworkTapType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the virtual network tap."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Location of the virtual network tap."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the virtual network tap."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the virtual network tap."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the virtual network tap.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "_2.ddosSettingsType": {
+ "type": "object",
+ "properties": {
+ "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": "Optional. The DDoS protection plan associated with the public IP address."
+ }
+ },
+ "protectionMode": {
+ "type": "string",
+ "allowedValues": [
+ "Enabled"
+ ],
+ "metadata": {
+ "description": "Required. The DDoS protection policy customizations."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0"
+ }
+ }
+ },
+ "_2.dnsSettingsType": {
+ "type": "object",
+ "properties": {
+ "domainNameLabel": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "domainNameLabelScope": {
+ "type": "string",
+ "allowedValues": [
+ "NoReuse",
+ "ResourceGroupReuse",
+ "SubscriptionReuse",
+ "TenantReuse"
+ ],
+ "nullable": true,
+ "metadata": {
+ "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_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0"
+ }
+ }
+ },
+ "_3.publicIPConfigurationType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the Public IP Address."
+ }
+ },
+ "publicIPAddressResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the public IP address."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Diagnostic settings for the public IP address."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The idle timeout in minutes."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the public IP address."
+ }
+ },
+ "idleTimeoutInMinutes": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The idle timeout of the public IP address."
+ }
+ },
+ "ddosSettings": {
+ "$ref": "#/definitions/_2.ddosSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DDoS protection plan configuration associated with the public IP address."
+ }
+ },
+ "dnsSettings": {
+ "$ref": "#/definitions/_2.dnsSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DNS settings of the public IP address."
+ }
+ },
+ "publicIPAddressVersion": {
+ "type": "string",
+ "allowedValues": [
+ "IPv4",
+ "IPv6"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The public IP address version."
+ }
+ },
+ "publicIPAllocationMethod": {
+ "type": "string",
+ "allowedValues": [
+ "Dynamic",
+ "Static"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The public IP address allocation method."
+ }
+ },
+ "publicIpPrefixResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "publicIpNameSuffix": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name suffix of the public IP address resource."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "allowedValues": [
+ "Basic",
+ "Standard"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The SKU name of the public IP address."
+ }
+ },
+ "skuTier": {
+ "type": "string",
+ "allowedValues": [
+ "Global",
+ "Regional"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The SKU tier of the public IP address."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags of the public IP address."
+ }
+ },
+ "zones": {
+ "type": "array",
+ "allowedValues": [
+ 1,
+ 2,
+ 3
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The zones of the public IP address."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for the module."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the public IP address configuration.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "modules/nic-configuration.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."
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "ipConfigurationType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the IP configuration."
+ }
+ },
+ "privateIPAllocationMethod": {
+ "type": "string",
+ "allowedValues": [
+ "Dynamic",
+ "Static"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address allocation method."
+ }
+ },
+ "privateIPAddress": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address."
+ }
+ },
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the subnet."
+ }
+ },
+ "loadBalancerBackendAddressPools": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.backendAddressPoolType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The load balancer backend address pools."
+ }
+ },
+ "applicationSecurityGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.applicationSecurityGroupType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The application security groups."
+ }
+ },
+ "applicationGatewayBackendAddressPools": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.applicationGatewayBackendAddressPoolsType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The application gateway backend address pools."
+ }
+ },
+ "gatewayLoadBalancer": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The gateway load balancer settings."
+ }
+ },
+ "loadBalancerInboundNatRules": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.inboundNatRuleType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The load balancer inbound NAT rules."
+ }
+ },
+ "privateIPAddressVersion": {
+ "type": "string",
+ "allowedValues": [
+ "IPv4",
+ "IPv6"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address version."
+ }
+ },
+ "virtualNetworkTaps": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.virtualNetworkTapType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The virtual network taps."
+ }
+ },
+ "pipConfiguration": {
+ "$ref": "#/definitions/_3.publicIPConfigurationType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The public IP address configuration."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the IP configuration."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags of the public IP address."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for the module."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the IP configuration.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "modules/nic-configuration.bicep"
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "networkInterfaceIPConfigurationOutputType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the IP configuration."
+ }
+ },
+ "privateIP": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The private IP address."
+ }
+ },
+ "publicIP": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The public IP address."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface: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"
+ }
+ }
+ },
+ "subResourceType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the sub resource."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the sub resource.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine to be created. You should use a unique prefix to reduce name collisions in Active Directory."
+ }
+ },
+ "computerName": {
+ "type": "string",
+ "defaultValue": "[parameters('name')]",
+ "metadata": {
+ "description": "Optional. Can be used if the computer name needs to be different from the Azure VM resource name. If not used, the resource name will be used as computer name."
+ }
+ },
+ "vmSize": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the size for the VMs."
+ }
+ },
+ "encryptionAtHost": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs."
+ }
+ },
+ "securityType": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "",
+ "ConfidentialVM",
+ "TrustedLaunch"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the SecurityType of the virtual machine. It has to be set to any specified value to enable UefiSettings. The default behavior is: UefiSettings will not be enabled unless this property is set."
+ }
+ },
+ "secureBootEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings."
+ }
+ },
+ "vTpmEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Specifies whether vTPM should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings."
+ }
+ },
+ "imageReference": {
+ "$ref": "#/definitions/imageReferenceType",
+ "metadata": {
+ "description": "Required. OS image reference. In case of marketplace images, it's the combination of the publisher, offer, sku, version attributes. In case of custom images it's the resource ID of the custom image."
+ }
+ },
+ "plan": {
+ "$ref": "#/definitions/planType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use."
+ }
+ },
+ "osDisk": {
+ "$ref": "#/definitions/osDiskType",
+ "metadata": {
+ "description": "Required. Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs."
+ }
+ },
+ "dataDisks": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/dataDiskType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs."
+ }
+ },
+ "ultraSSDEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled."
+ }
+ },
+ "hibernationEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. The flag that enables or disables hibernation capability on the VM."
+ }
+ },
+ "adminUsername": {
+ "type": "securestring",
+ "metadata": {
+ "description": "Required. Administrator username."
+ }
+ },
+ "adminPassword": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. When specifying a Windows Virtual Machine, this value should be passed."
+ }
+ },
+ "userData": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. UserData for the VM, which must be base-64 encoded. Customer should not pass any secrets in here."
+ }
+ },
+ "customData": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format."
+ }
+ },
+ "certificatesToBeInstalled": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/vaultSecretGroupType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies set of certificates that should be installed onto the virtual machine."
+ }
+ },
+ "priority": {
+ "type": "string",
+ "nullable": true,
+ "allowedValues": [
+ "Regular",
+ "Low",
+ "Spot"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the priority for the virtual machine."
+ }
+ },
+ "evictionPolicy": {
+ "type": "string",
+ "defaultValue": "Deallocate",
+ "allowedValues": [
+ "Deallocate",
+ "Delete"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the eviction policy for the low priority virtual machine."
+ }
+ },
+ "maxPriceForLowPriorityVm": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars."
+ }
+ },
+ "dedicatedHostId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Specifies resource ID about the dedicated host that the virtual machine resides in."
+ }
+ },
+ "licenseType": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "RHEL_BYOS",
+ "SLES_BYOS",
+ "Windows_Client",
+ "Windows_Server",
+ ""
+ ],
+ "metadata": {
+ "description": "Optional. Specifies that the image or disk that is being used was licensed on-premises."
+ }
+ },
+ "publicKeys": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/publicKeyType"
+ },
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The list of SSH public keys used to authenticate with linux based VMs."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource. The system-assigned managed identity will automatically be enabled if extensionAadJoinConfig.enabled = \"True\"."
+ }
+ },
+ "bootDiagnostics": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Whether boot diagnostics should be enabled on the Virtual Machine. Boot diagnostics will be enabled with a managed storage account if no bootDiagnosticsStorageAccountName value is provided. If bootDiagnostics and bootDiagnosticsStorageAccountName values are not provided, boot diagnostics will be disabled."
+ }
+ },
+ "bootDiagnosticStorageAccountName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Custom storage account used to store boot diagnostic information. Boot diagnostics will be enabled with a custom storage account if a value is provided."
+ }
+ },
+ "bootDiagnosticStorageAccountUri": {
+ "type": "string",
+ "defaultValue": "[format('.blob.{0}/', environment().suffixes.storage)]",
+ "metadata": {
+ "description": "Optional. Storage account boot diagnostic base URI."
+ }
+ },
+ "proximityPlacementGroupResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Resource ID of a proximity placement group."
+ }
+ },
+ "virtualMachineScaleSetResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Resource ID of a virtual machine scale set, where the VM should be added."
+ }
+ },
+ "availabilitySetResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set."
+ }
+ },
+ "galleryApplications": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/vmGalleryApplicationType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the gallery applications that should be made available to the VM/VMSS."
+ }
+ },
+ "availabilityZone": {
+ "type": "int",
+ "allowedValues": [
+ -1,
+ 1,
+ 2,
+ 3
+ ],
+ "metadata": {
+ "description": "Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If set to -1, no zone is defined. Note that the availability zone numbers here are the logical availability zone in your Azure subscription. Different subscriptions might have a different mapping of the physical zone and logical zone. To understand more, please refer to [Physical and logical availability zones](https://learn.microsoft.com/en-us/azure/reliability/availability-zones-overview?tabs=azure-cli#physical-and-logical-availability-zones)."
+ }
+ },
+ "nicConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/nicConfigurationType"
+ },
+ "metadata": {
+ "description": "Required. Configures NICs and PIPs."
+ }
+ },
+ "backupVaultName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Recovery service vault name to add VMs to backup."
+ }
+ },
+ "backupVaultResourceGroup": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().name]",
+ "metadata": {
+ "description": "Optional. Resource group of the backup recovery service vault. If not provided the current resource group name is considered by default."
+ }
+ },
+ "backupPolicyName": {
+ "type": "string",
+ "defaultValue": "DefaultPolicy",
+ "metadata": {
+ "description": "Optional. Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault."
+ }
+ },
+ "autoShutdownConfig": {
+ "$ref": "#/definitions/autoShutDownConfigType",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The configuration for auto-shutdown."
+ }
+ },
+ "maintenanceConfigurationResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The resource Id of a maintenance configuration for this VM."
+ }
+ },
+ "allowExtensionOperations": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Specifies whether extension operations should be allowed on the virtual machine. This may only be set to False when no extensions are present on the virtual machine."
+ }
+ },
+ "extensionDomainJoinPassword": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Required if name is specified. Password of the user specified in user parameter."
+ }
+ },
+ "extensionDomainJoinConfig": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The configuration for the [Domain Join] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionAadJoinConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [AAD Join] extension. Must at least contain the [\"enabled\": true] property to be executed. To enroll in Intune, add the setting mdmId: \"0000000a-0000-0000-c000-000000000000\"."
+ }
+ },
+ "extensionAntiMalwareConfig": {
+ "type": "object",
+ "defaultValue": "[if(equals(parameters('osType'), 'Windows'), createObject('enabled', true()), createObject('enabled', false()))]",
+ "metadata": {
+ "description": "Optional. The configuration for the [Anti Malware] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionMonitoringAgentConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false,
+ "dataCollectionRuleAssociations": []
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionDependencyAgentConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Dependency Agent] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionNetworkWatcherAgentConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Network Watcher Agent] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionAzureDiskEncryptionConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Azure Disk Encryption] extension. Must at least contain the [\"enabled\": true] property to be executed. Restrictions: Cannot be enabled on disks that have encryption at host enabled. Managed disks encrypted using Azure Disk Encryption cannot be encrypted using customer-managed keys."
+ }
+ },
+ "extensionDSCConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Desired State Configuration] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionCustomScriptConfig": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false,
+ "fileData": []
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Custom Script] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionNvidiaGpuDriverWindows": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Nvidia Gpu Driver Windows] extension. Must at least contain the [\"enabled\": true] property to be executed."
+ }
+ },
+ "extensionHostPoolRegistration": {
+ "type": "secureObject",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Host Pool Registration] extension. Must at least contain the [\"enabled\": true] property to be executed. Needs a managed identity."
+ }
+ },
+ "extensionGuestConfigurationExtension": {
+ "type": "object",
+ "defaultValue": {
+ "enabled": false
+ },
+ "metadata": {
+ "description": "Optional. The configuration for the [Guest Configuration] extension. Must at least contain the [\"enabled\": true] property to be executed. Needs a managed identity."
+ }
+ },
+ "guestConfiguration": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The guest configuration for the virtual machine. Needs the Guest Configuration extension to be enabled."
+ }
+ },
+ "extensionCustomScriptProtectedSetting": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. An object that contains the extension specific protected settings."
+ }
+ },
+ "extensionGuestConfigurationExtensionProtectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. An object that contains the extension specific protected settings."
+ }
+ },
+ "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 of the resource."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "baseTime": {
+ "type": "string",
+ "defaultValue": "[utcNow('u')]",
+ "metadata": {
+ "description": "Generated. Do not provide a value! This date value is used to generate a registration token."
+ }
+ },
+ "sasTokenValidityLength": {
+ "type": "string",
+ "defaultValue": "PT8H",
+ "metadata": {
+ "description": "Optional. SAS token validity length to use to download files from storage accounts. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours."
+ }
+ },
+ "osType": {
+ "type": "string",
+ "allowedValues": [
+ "Windows",
+ "Linux"
+ ],
+ "metadata": {
+ "description": "Required. The chosen OS type."
+ }
+ },
+ "disablePasswordAuthentication": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Specifies whether password authentication should be disabled."
+ }
+ },
+ "provisionVMAgent": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later."
+ }
+ },
+ "enableAutomaticUpdates": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. When patchMode is set to Manual, this parameter must be set to false. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning."
+ }
+ },
+ "patchMode": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "AutomaticByPlatform",
+ "AutomaticByOS",
+ "Manual",
+ "ImageDefault",
+ ""
+ ],
+ "metadata": {
+ "description": "Optional. VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'."
+ }
+ },
+ "bypassPlatformSafetyChecksOnUserSchedule": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enables customer to schedule patching without accidental upgrades."
+ }
+ },
+ "rebootSetting": {
+ "type": "string",
+ "defaultValue": "IfRequired",
+ "allowedValues": [
+ "Always",
+ "IfRequired",
+ "Never",
+ "Unknown"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations."
+ }
+ },
+ "patchAssessmentMode": {
+ "type": "string",
+ "defaultValue": "ImageDefault",
+ "allowedValues": [
+ "AutomaticByPlatform",
+ "ImageDefault"
+ ],
+ "metadata": {
+ "description": "Optional. VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours."
+ }
+ },
+ "enableHotpatching": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enables customers to patch their Azure VMs without requiring a reboot. For enableHotpatching, the 'provisionVMAgent' must be set to true and 'patchMode' must be set to 'AutomaticByPlatform'."
+ }
+ },
+ "timeZone": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Specifies the time zone of the virtual machine. e.g. 'Pacific Standard Time'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`."
+ }
+ },
+ "additionalUnattendContent": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/additionalUnattendContentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies additional XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. Contents are defined by setting name, component name, and the pass in which the content is applied."
+ }
+ },
+ "winRMListeners": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/winRMListenerType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell."
+ }
+ },
+ "configurationProfile": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The configuration profile of automanage. Either '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction', 'providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' or the resource Id of custom profile."
+ }
+ },
+ "capacityReservationGroupResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Capacity reservation group resource id that should be used for allocating the virtual machine vm instances provided enough capacity has been reserved."
+ }
+ },
+ "networkAccessPolicy": {
+ "type": "string",
+ "defaultValue": "DenyAll",
+ "allowedValues": [
+ "AllowAll",
+ "AllowPrivate",
+ "DenyAll"
+ ],
+ "metadata": {
+ "description": "Optional. Policy for accessing the disk via network."
+ }
+ },
+ "publicNetworkAccess": {
+ "type": "string",
+ "defaultValue": "Disabled",
+ "allowedValues": [
+ "Disabled",
+ "Enabled"
+ ],
+ "metadata": {
+ "description": "Optional. Policy for controlling export on the disk."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "publicKeysFormatted",
+ "count": "[length(parameters('publicKeys'))]",
+ "input": {
+ "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]",
+ "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]"
+ }
+ },
+ {
+ "name": "additionalUnattendContentFormatted",
+ "count": "[length(coalesce(parameters('additionalUnattendContent'), createArray()))]",
+ "input": {
+ "settingName": "[coalesce(parameters('additionalUnattendContent'), createArray())[copyIndex('additionalUnattendContentFormatted')].settingName]",
+ "content": "[coalesce(parameters('additionalUnattendContent'), createArray())[copyIndex('additionalUnattendContentFormatted')].content]",
+ "componentName": "Microsoft-Windows-Shell-Setup",
+ "passName": "OobeSystem"
+ }
+ },
+ {
+ "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,
+ "linuxConfiguration": {
+ "disablePasswordAuthentication": "[parameters('disablePasswordAuthentication')]",
+ "ssh": {
+ "publicKeys": "[variables('publicKeysFormatted')]"
+ },
+ "provisionVMAgent": "[parameters('provisionVMAgent')]",
+ "patchSettings": "[if(and(parameters('provisionVMAgent'), or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('ImageDefault')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]"
+ },
+ "windowsConfiguration": {
+ "provisionVMAgent": "[parameters('provisionVMAgent')]",
+ "enableAutomaticUpdates": "[parameters('enableAutomaticUpdates')]",
+ "patchSettings": "[if(and(parameters('provisionVMAgent'), or(or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('AutomaticByOS'))), equals(toLower(parameters('patchMode')), toLower('Manual')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'enableHotpatching', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), parameters('enableHotpatching'), false()), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]",
+ "timeZone": "[if(empty(parameters('timeZone')), null(), parameters('timeZone'))]",
+ "additionalUnattendContent": "[if(empty(parameters('additionalUnattendContent')), null(), variables('additionalUnattendContentFormatted'))]",
+ "winRM": "[if(not(empty(parameters('winRMListeners'))), createObject('listeners', parameters('winRMListeners')), null())]"
+ },
+ "accountSasProperties": {
+ "signedServices": "b",
+ "signedPermission": "r",
+ "signedExpiry": "[dateTimeAdd(parameters('baseTime'), parameters('sasTokenValidityLength'))]",
+ "signedResourceTypes": "o",
+ "signedProtocol": "https"
+ },
+ "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(if(parameters('extensionAadJoinConfig').enabled, true(), 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": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]",
+ "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]",
+ "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]",
+ "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]",
+ "DevTest Labs User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64')]",
+ "Disk Backup Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e5e47e6-65f7-47ef-90b5-e5dd4d455f24')]",
+ "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]",
+ "Disk Restore Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b50d9833-a0cb-478e-945f-707fcc997c13')]",
+ "Disk Snapshot Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce')]",
+ "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')]",
+ "Virtual Machine Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]",
+ "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]",
+ "Virtual Machine User Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52')]",
+ "VM Scanner Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd24ecba3-c1f4-40fa-a7bb-4588a071e8fd')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.compute-virtualmachine.{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"
+ }
+ }
+ }
+ }
+ },
+ "managedDataDisks": {
+ "copy": {
+ "name": "managedDataDisks",
+ "count": "[length(coalesce(parameters('dataDisks'), createArray()))]"
+ },
+ "condition": "[empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()].managedDisk, 'id'))]",
+ "type": "Microsoft.Compute/disks",
+ "apiVersion": "2024-03-02",
+ "name": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex(), 1), 2, '0')))]",
+ "location": "[parameters('location')]",
+ "sku": {
+ "name": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()].managedDisk, 'storageAccountType')]"
+ },
+ "properties": {
+ "diskSizeGB": "[coalesce(parameters('dataDisks'), createArray())[copyIndex()].diskSizeGB]",
+ "creationData": {
+ "createOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'createoption'), 'Empty')]"
+ },
+ "diskIOPSReadWrite": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'diskIOPSReadWrite')]",
+ "diskMBpsReadWrite": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'diskMBpsReadWrite')]",
+ "publicNetworkAccess": "[parameters('publicNetworkAccess')]",
+ "networkAccessPolicy": "[parameters('networkAccessPolicy')]"
+ },
+ "zones": "[if(and(not(equals(parameters('availabilityZone'), -1)), not(contains(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()].managedDisk, 'storageAccountType'), 'ZRS'))), array(string(parameters('availabilityZone'))), null())]",
+ "tags": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "vm": {
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2024-07-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "identity": "[variables('identity')]",
+ "tags": "[parameters('tags')]",
+ "zones": "[if(not(equals(parameters('availabilityZone'), -1)), array(string(parameters('availabilityZone'))), null())]",
+ "plan": "[parameters('plan')]",
+ "properties": {
+ "hardwareProfile": {
+ "vmSize": "[parameters('vmSize')]"
+ },
+ "securityProfile": {
+ "encryptionAtHost": "[if(parameters('encryptionAtHost'), parameters('encryptionAtHost'), null())]",
+ "securityType": "[parameters('securityType')]",
+ "uefiSettings": "[if(equals(parameters('securityType'), 'TrustedLaunch'), createObject('secureBootEnabled', parameters('secureBootEnabled'), 'vTpmEnabled', parameters('vTpmEnabled')), null())]"
+ },
+ "storageProfile": {
+ "copy": [
+ {
+ "name": "dataDisks",
+ "count": "[length(coalesce(parameters('dataDisks'), createArray()))]",
+ "input": {
+ "lun": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'lun'), copyIndex('dataDisks'))]",
+ "name": "[if(not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'))), last(split(coalesce(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.id, ''), '/')), coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0'))))]",
+ "createOption": "[if(or(not(equals(if(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id')), resourceId('Microsoft.Compute/disks', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))), null()), null())), not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id')))), 'Attach', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'createoption'), 'Empty'))]",
+ "deleteOption": "[if(not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'))), 'Detach', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'deleteOption'), 'Delete'))]",
+ "caching": "[if(not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'))), 'None', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'caching'), 'ReadOnly'))]",
+ "managedDisk": {
+ "id": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'), if(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id')), resourceId('Microsoft.Compute/disks', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))), null()))]",
+ "diskEncryptionSet": "[if(contains(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'diskEncryptionSet'), createObject('id', coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.diskEncryptionSet.id), null())]"
+ }
+ }
+ }
+ ],
+ "imageReference": "[parameters('imageReference')]",
+ "osDisk": {
+ "name": "[coalesce(tryGet(parameters('osDisk'), 'name'), format('{0}-disk-os-01', parameters('name')))]",
+ "createOption": "[coalesce(tryGet(parameters('osDisk'), 'createOption'), 'FromImage')]",
+ "deleteOption": "[coalesce(tryGet(parameters('osDisk'), 'deleteOption'), 'Delete')]",
+ "diffDiskSettings": "[if(empty(coalesce(tryGet(parameters('osDisk'), 'diffDiskSettings'), createObject())), null(), createObject('option', 'Local', 'placement', parameters('osDisk').diffDiskSettings.placement))]",
+ "diskSizeGB": "[tryGet(parameters('osDisk'), 'diskSizeGB')]",
+ "caching": "[coalesce(tryGet(parameters('osDisk'), 'caching'), 'ReadOnly')]",
+ "managedDisk": {
+ "storageAccountType": "[tryGet(parameters('osDisk').managedDisk, 'storageAccountType')]",
+ "diskEncryptionSet": {
+ "id": "[tryGet(parameters('osDisk').managedDisk, 'diskEncryptionSetResourceId')]"
+ }
+ }
+ }
+ },
+ "additionalCapabilities": {
+ "ultraSSDEnabled": "[parameters('ultraSSDEnabled')]",
+ "hibernationEnabled": "[parameters('hibernationEnabled')]"
+ },
+ "osProfile": {
+ "computerName": "[parameters('computerName')]",
+ "adminUsername": "[parameters('adminUsername')]",
+ "adminPassword": "[parameters('adminPassword')]",
+ "customData": "[if(not(empty(parameters('customData'))), base64(parameters('customData')), null())]",
+ "windowsConfiguration": "[if(equals(parameters('osType'), 'Windows'), variables('windowsConfiguration'), null())]",
+ "linuxConfiguration": "[if(equals(parameters('osType'), 'Linux'), variables('linuxConfiguration'), null())]",
+ "secrets": "[parameters('certificatesToBeInstalled')]",
+ "allowExtensionOperations": "[parameters('allowExtensionOperations')]"
+ },
+ "networkProfile": {
+ "copy": [
+ {
+ "name": "networkInterfaces",
+ "count": "[length(parameters('nicConfigurations'))]",
+ "input": {
+ "properties": {
+ "deleteOption": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'deleteOption'), 'Delete')]",
+ "primary": "[if(equals(copyIndex('networkInterfaces'), 0), true(), false())]"
+ },
+ "id": "[resourceId('Microsoft.Network/networkInterfaces', coalesce(tryGet(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'name'), format('{0}{1}', parameters('name'), tryGet(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'nicSuffix'))))]"
+ }
+ }
+ ]
+ },
+ "capacityReservation": "[if(not(empty(parameters('capacityReservationGroupResourceId'))), createObject('capacityReservationGroup', createObject('id', parameters('capacityReservationGroupResourceId'))), null())]",
+ "diagnosticsProfile": {
+ "bootDiagnostics": {
+ "enabled": "[if(not(empty(parameters('bootDiagnosticStorageAccountName'))), true(), parameters('bootDiagnostics'))]",
+ "storageUri": "[if(not(empty(parameters('bootDiagnosticStorageAccountName'))), format('https://{0}{1}', parameters('bootDiagnosticStorageAccountName'), parameters('bootDiagnosticStorageAccountUri')), null())]"
+ }
+ },
+ "applicationProfile": "[if(not(empty(parameters('galleryApplications'))), createObject('galleryApplications', parameters('galleryApplications')), null())]",
+ "availabilitySet": "[if(not(empty(parameters('availabilitySetResourceId'))), createObject('id', parameters('availabilitySetResourceId')), null())]",
+ "proximityPlacementGroup": "[if(not(empty(parameters('proximityPlacementGroupResourceId'))), createObject('id', parameters('proximityPlacementGroupResourceId')), null())]",
+ "virtualMachineScaleSet": "[if(not(empty(parameters('virtualMachineScaleSetResourceId'))), createObject('id', parameters('virtualMachineScaleSetResourceId')), null())]",
+ "priority": "[parameters('priority')]",
+ "evictionPolicy": "[if(and(not(empty(parameters('priority'))), not(equals(parameters('priority'), 'Regular'))), parameters('evictionPolicy'), null())]",
+ "billingProfile": "[if(and(not(empty(parameters('priority'))), not(empty(parameters('maxPriceForLowPriorityVm')))), createObject('maxPrice', json(parameters('maxPriceForLowPriorityVm'))), null())]",
+ "host": "[if(not(empty(parameters('dedicatedHostId'))), createObject('id', parameters('dedicatedHostId')), null())]",
+ "licenseType": "[if(not(empty(parameters('licenseType'))), parameters('licenseType'), null())]",
+ "userData": "[if(not(empty(parameters('userData'))), base64(parameters('userData')), null())]"
+ },
+ "dependsOn": [
+ "managedDataDisks",
+ "vm_nic"
+ ]
+ },
+ "vm_configurationAssignment": {
+ "condition": "[not(empty(parameters('maintenanceConfigurationResourceId')))]",
+ "type": "Microsoft.Maintenance/configurationAssignments",
+ "apiVersion": "2023-04-01",
+ "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]",
+ "name": "[format('{0}assignment', parameters('name'))]",
+ "location": "[parameters('location')]",
+ "properties": {
+ "maintenanceConfigurationId": "[parameters('maintenanceConfigurationResourceId')]",
+ "resourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]"
+ },
+ "dependsOn": [
+ "vm"
+ ]
+ },
+ "vm_configurationProfileAssignment": {
+ "condition": "[not(empty(parameters('configurationProfile')))]",
+ "type": "Microsoft.Automanage/configurationProfileAssignments",
+ "apiVersion": "2022-05-04",
+ "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]",
+ "name": "default",
+ "properties": {
+ "configurationProfile": "[parameters('configurationProfile')]"
+ },
+ "dependsOn": [
+ "vm"
+ ]
+ },
+ "vm_autoShutdownConfiguration": {
+ "condition": "[not(empty(parameters('autoShutdownConfig')))]",
+ "type": "Microsoft.DevTestLab/schedules",
+ "apiVersion": "2018-09-15",
+ "name": "[format('shutdown-computevm-{0}', parameters('name'))]",
+ "location": "[parameters('location')]",
+ "properties": {
+ "status": "[coalesce(tryGet(parameters('autoShutdownConfig'), 'status'), 'Disabled')]",
+ "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]",
+ "taskType": "ComputeVmShutdownTask",
+ "dailyRecurrence": {
+ "time": "[coalesce(tryGet(parameters('autoShutdownConfig'), 'dailyRecurrenceTime'), '19:00')]"
+ },
+ "timeZoneId": "[coalesce(tryGet(parameters('autoShutdownConfig'), 'timeZone'), 'UTC')]",
+ "notificationSettings": "[if(contains(parameters('autoShutdownConfig'), 'notificationSettings'), createObject('status', coalesce(tryGet(parameters('autoShutdownConfig'), 'status'), 'Disabled'), 'emailRecipient', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'emailRecipient'), ''), 'notificationLocale', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'notificationLocale'), 'en'), 'webhookUrl', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'webhookUrl'), ''), 'timeInMinutes', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'timeInMinutes'), 30)), null())]"
+ },
+ "dependsOn": [
+ "vm"
+ ]
+ },
+ "vm_dataCollectionRuleAssociations": {
+ "copy": {
+ "name": "vm_dataCollectionRuleAssociations",
+ "count": "[length(parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations)]"
+ },
+ "condition": "[parameters('extensionMonitoringAgentConfig').enabled]",
+ "type": "Microsoft.Insights/dataCollectionRuleAssociations",
+ "apiVersion": "2023-03-11",
+ "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]",
+ "name": "[parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations[copyIndex()].name]",
+ "properties": {
+ "dataCollectionRuleId": "[parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations[copyIndex()].dataCollectionRuleResourceId]"
+ },
+ "dependsOn": [
+ "vm",
+ "vm_azureMonitorAgentExtension"
+ ]
+ },
+ "AzureWindowsBaseline": {
+ "condition": "[not(empty(parameters('guestConfiguration')))]",
+ "type": "Microsoft.GuestConfiguration/guestConfigurationAssignments",
+ "apiVersion": "2020-06-25",
+ "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('guestConfiguration'), 'name'), 'AzureWindowsBaseline')]",
+ "location": "[parameters('location')]",
+ "properties": {
+ "guestConfiguration": "[parameters('guestConfiguration')]"
+ },
+ "dependsOn": [
+ "vm",
+ "vm_azureGuestConfigurationExtension"
+ ]
+ },
+ "vm_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.Compute/virtualMachines/{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": [
+ "vm"
+ ]
+ },
+ "vm_roleAssignments": {
+ "copy": {
+ "name": "vm_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/virtualMachines', 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": [
+ "vm"
+ ]
+ },
+ "vm_nic": {
+ "copy": {
+ "name": "vm_nic",
+ "count": "[length(parameters('nicConfigurations'))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-Nic-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "networkInterfaceName": {
+ "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'name'), format('{0}{1}', parameters('name'), tryGet(parameters('nicConfigurations')[copyIndex()], 'nicSuffix')))]"
+ },
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "enableIPForwarding": {
+ "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), false())]"
+ },
+ "enableAcceleratedNetworking": {
+ "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'enableAcceleratedNetworking'), true())]"
+ },
+ "dnsServers": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'dnsServers'), if(not(empty(tryGet(parameters('nicConfigurations')[copyIndex()], 'dnsServers'))), createObject('value', tryGet(parameters('nicConfigurations')[copyIndex()], 'dnsServers')), createObject('value', createArray())), createObject('value', createArray()))]",
+ "networkSecurityGroupResourceId": {
+ "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'networkSecurityGroupResourceId'), '')]"
+ },
+ "ipConfigurations": {
+ "value": "[parameters('nicConfigurations')[copyIndex()].ipConfigurations]"
+ },
+ "lock": {
+ "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'lock'), parameters('lock'))]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "diagnosticSettings": {
+ "value": "[tryGet(parameters('nicConfigurations')[copyIndex()], 'diagnosticSettings')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(parameters('nicConfigurations')[copyIndex()], 'roleAssignments')]"
+ },
+ "enableTelemetry": {
+ "value": "[variables('enableReferencedModulesTelemetry')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "2776867808756314911"
+ }
+ },
+ "definitions": {
+ "publicIPConfigurationType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the Public IP Address."
+ }
+ },
+ "publicIPAddressResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the public IP address."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Diagnostic settings for the public IP address."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The idle timeout in minutes."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the public IP address."
+ }
+ },
+ "idleTimeoutInMinutes": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The idle timeout of the public IP address."
+ }
+ },
+ "ddosSettings": {
+ "$ref": "#/definitions/ddosSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DDoS protection plan configuration associated with the public IP address."
+ }
+ },
+ "dnsSettings": {
+ "$ref": "#/definitions/dnsSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DNS settings of the public IP address."
+ }
+ },
+ "publicIPAddressVersion": {
+ "type": "string",
+ "allowedValues": [
+ "IPv4",
+ "IPv6"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The public IP address version."
+ }
+ },
+ "publicIPAllocationMethod": {
+ "type": "string",
+ "allowedValues": [
+ "Dynamic",
+ "Static"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The public IP address allocation method."
+ }
+ },
+ "publicIpPrefixResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "publicIpNameSuffix": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name suffix of the public IP address resource."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "allowedValues": [
+ "Basic",
+ "Standard"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The SKU name of the public IP address."
+ }
+ },
+ "skuTier": {
+ "type": "string",
+ "allowedValues": [
+ "Global",
+ "Regional"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The SKU tier of the public IP address."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags of the public IP address."
+ }
+ },
+ "zones": {
+ "type": "array",
+ "allowedValues": [
+ 1,
+ 2,
+ 3
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The zones of the public IP address."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for the module."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the public IP address configuration."
+ }
+ },
+ "ipConfigurationType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the IP configuration."
+ }
+ },
+ "privateIPAllocationMethod": {
+ "type": "string",
+ "allowedValues": [
+ "Dynamic",
+ "Static"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address allocation method."
+ }
+ },
+ "privateIPAddress": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address."
+ }
+ },
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the subnet."
+ }
+ },
+ "loadBalancerBackendAddressPools": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/backendAddressPoolType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The load balancer backend address pools."
+ }
+ },
+ "applicationSecurityGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/applicationSecurityGroupType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The application security groups."
+ }
+ },
+ "applicationGatewayBackendAddressPools": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/applicationGatewayBackendAddressPoolsType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The application gateway backend address pools."
+ }
+ },
+ "gatewayLoadBalancer": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The gateway load balancer settings."
+ }
+ },
+ "loadBalancerInboundNatRules": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/inboundNatRuleType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The load balancer inbound NAT rules."
+ }
+ },
+ "privateIPAddressVersion": {
+ "type": "string",
+ "allowedValues": [
+ "IPv4",
+ "IPv6"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address version."
+ }
+ },
+ "virtualNetworkTaps": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/virtualNetworkTapType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The virtual network taps."
+ }
+ },
+ "pipConfiguration": {
+ "$ref": "#/definitions/publicIPConfigurationType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The public IP address configuration."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the IP configuration."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tags of the public IP address."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for the module."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the IP configuration."
+ }
+ },
+ "applicationGatewayBackendAddressPoolsType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the backend address pool."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the backend address pool that is unique within an Application Gateway."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "backendAddresses": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "ipAddress": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. IP address of the backend address."
+ }
+ },
+ "fqdn": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. FQDN of the backend address."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Backend addresses."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the application gateway backend address pool."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the application gateway backend address pool.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "applicationSecurityGroupType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the application security group."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Location of the application security group."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the application security group."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the application security group."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the application security group.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "backendAddressPoolType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the backend address pool."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the backend address pool."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The properties of the backend address pool."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a backend address pool.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "ddosSettingsType": {
+ "type": "object",
+ "properties": {
+ "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": "Optional. The DDoS protection plan associated with the public IP address."
+ }
+ },
+ "protectionMode": {
+ "type": "string",
+ "allowedValues": [
+ "Enabled"
+ ],
+ "metadata": {
+ "description": "Required. The DDoS protection policy customizations."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.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.5.1"
+ }
+ }
+ },
+ "dnsSettingsType": {
+ "type": "object",
+ "properties": {
+ "domainNameLabel": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "domainNameLabelScope": {
+ "type": "string",
+ "allowedValues": [
+ "NoReuse",
+ "ResourceGroupReuse",
+ "SubscriptionReuse",
+ "TenantReuse"
+ ],
+ "nullable": true,
+ "metadata": {
+ "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_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0"
+ }
+ }
+ },
+ "inboundNatRuleType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the inbound NAT rule."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the resource that is unique within the set of inbound NAT rules used by the load balancer. This name can be used to access the resource."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "backendAddressPool": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A reference to backendAddressPool resource."
+ }
+ },
+ "backendPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port used for the internal endpoint. Acceptable values range from 1 to 65535."
+ }
+ },
+ "enableFloatingIP": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint."
+ }
+ },
+ "enableTcpReset": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP."
+ }
+ },
+ "frontendIPConfiguration": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A reference to frontend IP addresses."
+ }
+ },
+ "frontendPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. Acceptable values range from 1 to 65534."
+ }
+ },
+ "frontendPortRangeStart": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534."
+ }
+ },
+ "frontendPortRangeEnd": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534."
+ }
+ },
+ "protocol": {
+ "type": "string",
+ "allowedValues": [
+ "All",
+ "Tcp",
+ "Udp"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reference to the transport protocol used by the load balancing rule."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the inbound NAT rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the inbound NAT rule.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "ipTagType": {
+ "type": "object",
+ "properties": {
+ "ipTagType": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The IP tag type."
+ }
+ },
+ "tag": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The IP tag."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.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.5.1"
+ }
+ }
+ },
+ "networkInterfaceIPConfigurationOutputType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the IP configuration."
+ }
+ },
+ "privateIP": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The private IP address."
+ }
+ },
+ "publicIP": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The public IP address."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface: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"
+ }
+ }
+ },
+ "subResourceType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the sub resource."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the sub resource.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ },
+ "virtualNetworkTapType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the virtual network tap."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Location of the virtual network tap."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the virtual network tap."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the virtual network tap."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the virtual network tap.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "networkInterfaceName": {
+ "type": "string"
+ },
+ "virtualMachineName": {
+ "type": "string"
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ipConfigurationType"
+ }
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "enableIPForwarding": {
+ "type": "bool",
+ "defaultValue": false
+ },
+ "enableAcceleratedNetworking": {
+ "type": "bool",
+ "defaultValue": false
+ },
+ "dnsServers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "defaultValue": []
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Enable telemetry via a Globally Unique Identifier (GUID)."
+ }
+ },
+ "networkSecurityGroupResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The network security group (NSG) to attach to the network interface."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ }
+ },
+ "resources": {
+ "networkInterface_publicIPAddresses": {
+ "copy": {
+ "name": "networkInterface_publicIPAddresses",
+ "count": "[length(parameters('ipConfigurations'))]"
+ },
+ "condition": "[and(not(empty(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'))), empty(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIPAddressResourceId')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-publicIP-{1}', deployment().name, copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'name'), format('{0}{1}', parameters('virtualMachineName'), tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIpNameSuffix')))]"
+ },
+ "diagnosticSettings": {
+ "value": "[coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'diagnosticSettings'), tryGet(parameters('ipConfigurations')[copyIndex()], 'diagnosticSettings'))]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "lock": {
+ "value": "[parameters('lock')]"
+ },
+ "idleTimeoutInMinutes": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'idleTimeoutInMinutes')]"
+ },
+ "ddosSettings": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'ddosSettings')]"
+ },
+ "dnsSettings": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'dnsSettings')]"
+ },
+ "publicIPAddressVersion": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIPAddressVersion')]"
+ },
+ "publicIPAllocationMethod": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIPAllocationMethod')]"
+ },
+ "publicIpPrefixResourceId": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIpPrefixResourceId')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'roleAssignments')]"
+ },
+ "skuName": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'skuName')]"
+ },
+ "skuTier": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'skuTier')]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "zones": {
+ "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'zones')]"
+ },
+ "enableTelemetry": {
+ "value": "[coalesce(coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'enableTelemetry'), tryGet(parameters('ipConfigurations')[copyIndex()], 'enableTelemetry')), parameters('enableTelemetry'))]"
+ }
+ },
+ "template": {
+ "$schema": "https://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": {
+ "domainNameLabel": {
+ "type": "string",
+ "metadata": {
+ "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."
+ }
+ },
+ "domainNameLabelScope": {
+ "type": "string",
+ "allowedValues": [
+ "NoReuse",
+ "ResourceGroupReuse",
+ "SubscriptionReuse",
+ "TenantReuse"
+ ],
+ "nullable": true,
+ "metadata": {
+ "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
+ }
+ },
+ "ddosSettingsType": {
+ "type": "object",
+ "properties": {
+ "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": "Optional. The DDoS protection plan associated with the public IP address."
+ }
+ },
+ "protectionMode": {
+ "type": "string",
+ "allowedValues": [
+ "Enabled"
+ ],
+ "metadata": {
+ "description": "Required. The DDoS protection policy customizations."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true
+ }
+ },
+ "ipTagType": {
+ "type": "object",
+ "properties": {
+ "ipTagType": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The IP tag type."
+ }
+ },
+ "tag": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The IP tag."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true
+ }
+ },
+ "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.2.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.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\"."
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the Public IP Address."
+ }
+ },
+ "publicIpPrefixResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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."
+ }
+ },
+ "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 DNS settings of the public IP address."
+ }
+ },
+ "ipTags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ipTagType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The list of tags associated with the public IP address."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "defaultValue": "Standard",
+ "allowedValues": [
+ "Basic",
+ "Standard"
+ ],
+ "metadata": {
+ "description": "Optional. Name of a public IP address SKU."
+ }
+ },
+ "skuTier": {
+ "type": "string",
+ "defaultValue": "Regional",
+ "allowedValues": [
+ "Global",
+ "Regional"
+ ],
+ "metadata": {
+ "description": "Optional. Tier of a public IP address SKU."
+ }
+ },
+ "ddosSettings": {
+ "$ref": "#/definitions/ddosSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DDoS protection plan configuration associated with the public IP address."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "idleTimeoutInMinutes": {
+ "type": "int",
+ "defaultValue": 4,
+ "metadata": {
+ "description": "Optional. The idle timeout of the public IP address."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "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')]",
+ "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-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"
+ }
+ }
+ }
+ }
+ },
+ "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": {
+ "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')]"
+ }
+ },
+ "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": "publicIpAddress_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "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]",
+ "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": [
+ "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 public IP address."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the public IP address."
+ },
+ "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]"
+ },
+ "ipAddress": {
+ "type": "string",
+ "metadata": {
+ "description": "The public IP address of the public IP address resource."
+ },
+ "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]"
+ }
+ }
+ }
+ }
+ },
+ "networkInterface": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-NetworkInterface', deployment().name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[parameters('networkInterfaceName')]"
+ },
+ "ipConfigurations": {
+ "copy": [
+ {
+ "name": "value",
+ "count": "[length(parameters('ipConfigurations'))]",
+ "input": "[createObject('name', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'name'), 'privateIPAllocationMethod', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), 'privateIPAddress', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), 'publicIPAddressResourceId', if(not(empty(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'))), if(not(contains(coalesce(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), createObject()), 'publicIPAddressResourceId')), resourceId('Microsoft.Network/publicIPAddresses', coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), 'name'), format('{0}{1}', parameters('virtualMachineName'), tryGet(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), 'publicIpNameSuffix')))), tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration', 'publicIPAddressResourceId')), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), 'applicationSecurityGroups', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), 'applicationGatewayBackendAddressPools', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), 'gatewayLoadBalancer', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), 'loadBalancerInboundNatRules', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), 'privateIPAddressVersion', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), 'virtualNetworkTaps', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'))]"
+ }
+ ]
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "diagnosticSettings": {
+ "value": "[parameters('diagnosticSettings')]"
+ },
+ "dnsServers": {
+ "value": "[parameters('dnsServers')]"
+ },
+ "enableAcceleratedNetworking": {
+ "value": "[parameters('enableAcceleratedNetworking')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "enableIPForwarding": {
+ "value": "[parameters('enableIPForwarding')]"
+ },
+ "lock": {
+ "value": "[parameters('lock')]"
+ },
+ "networkSecurityGroupResourceId": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('value', parameters('networkSecurityGroupResourceId')), createObject('value', ''))]",
+ "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.34.44.8038",
+ "templateHash": "8196054567469390015"
+ },
+ "name": "Network Interface",
+ "description": "This module deploys a Network Interface."
+ },
+ "definitions": {
+ "networkInterfaceIPConfigurationType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the IP configuration."
+ }
+ },
+ "privateIPAllocationMethod": {
+ "type": "string",
+ "allowedValues": [
+ "Dynamic",
+ "Static"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address allocation method."
+ }
+ },
+ "privateIPAddress": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private IP address."
+ }
+ },
+ "publicIPAddressResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the public IP address."
+ }
+ },
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the subnet."
+ }
+ },
+ "loadBalancerBackendAddressPools": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/backendAddressPoolType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of load balancer backend address pools."
+ }
+ },
+ "loadBalancerInboundNatRules": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/inboundNatRuleType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A list of references of LoadBalancerInboundNatRules."
+ }
+ },
+ "applicationSecurityGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/applicationSecurityGroupType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Application security groups in which the IP configuration is included."
+ }
+ },
+ "applicationGatewayBackendAddressPools": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/applicationGatewayBackendAddressPoolsType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reference to Application Gateway Backend Address Pools."
+ }
+ },
+ "gatewayLoadBalancer": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reference to gateway load balancer frontend IP."
+ }
+ },
+ "privateIPAddressVersion": {
+ "type": "string",
+ "allowedValues": [
+ "IPv4",
+ "IPv6"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Whether the specific IP configuration is IPv4 or IPv6."
+ }
+ },
+ "virtualNetworkTaps": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/virtualNetworkTapType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reference to Virtual Network Taps."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The resource ID of the deployed resource."
+ }
+ },
+ "backendAddressPoolType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the backend address pool."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the backend address pool."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The properties of the backend address pool."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a backend address pool."
+ }
+ },
+ "applicationSecurityGroupType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the application security group."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Location of the application security group."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the application security group."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the application security group."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the application security group."
+ }
+ },
+ "applicationGatewayBackendAddressPoolsType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the backend address pool."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the backend address pool that is unique within an Application Gateway."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "backendAddresses": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "ipAddress": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. IP address of the backend address."
+ }
+ },
+ "fqdn": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. FQDN of the backend address."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Backend addresses."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the application gateway backend address pool."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the application gateway backend address pool."
+ }
+ },
+ "subResourceType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the sub resource."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the sub resource."
+ }
+ },
+ "inboundNatRuleType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the inbound NAT rule."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the resource that is unique within the set of inbound NAT rules used by the load balancer. This name can be used to access the resource."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "backendAddressPool": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A reference to backendAddressPool resource."
+ }
+ },
+ "backendPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port used for the internal endpoint. Acceptable values range from 1 to 65535."
+ }
+ },
+ "enableFloatingIP": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint."
+ }
+ },
+ "enableTcpReset": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP."
+ }
+ },
+ "frontendIPConfiguration": {
+ "$ref": "#/definitions/subResourceType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A reference to frontend IP addresses."
+ }
+ },
+ "frontendPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. Acceptable values range from 1 to 65534."
+ }
+ },
+ "frontendPortRangeStart": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534."
+ }
+ },
+ "frontendPortRangeEnd": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534."
+ }
+ },
+ "protocol": {
+ "type": "string",
+ "allowedValues": [
+ "All",
+ "Tcp",
+ "Udp"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reference to the transport protocol used by the load balancing rule."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the inbound NAT rule."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the inbound NAT rule."
+ }
+ },
+ "virtualNetworkTapType": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the virtual network tap."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Location of the virtual network tap."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Properties of the virtual network tap."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the virtual network tap."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the virtual network tap."
+ }
+ },
+ "networkInterfaceIPConfigurationOutputType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the IP configuration."
+ }
+ },
+ "privateIP": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The private IP address."
+ }
+ },
+ "publicIP": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The public IP address."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "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 network interface."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource tags."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "enableIPForwarding": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether IP forwarding is enabled on this network interface."
+ }
+ },
+ "enableAcceleratedNetworking": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. If the network interface is accelerated networking enabled."
+ }
+ },
+ "dnsServers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. List of DNS servers IP addresses. Use 'AzureProvidedDNS' to switch to azure provided DNS resolution. 'AzureProvidedDNS' value cannot be combined with other IPs, it must be the only value in dnsServers collection."
+ }
+ },
+ "networkSecurityGroupResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The network security group (NSG) to attach to the network interface."
+ }
+ },
+ "auxiliaryMode": {
+ "type": "string",
+ "defaultValue": "None",
+ "allowedValues": [
+ "Floating",
+ "MaxConnections",
+ "None"
+ ],
+ "metadata": {
+ "description": "Optional. Auxiliary mode of Network Interface resource. Not all regions are enabled for Auxiliary Mode Nic."
+ }
+ },
+ "auxiliarySku": {
+ "type": "string",
+ "defaultValue": "None",
+ "allowedValues": [
+ "A1",
+ "A2",
+ "A4",
+ "A8",
+ "None"
+ ],
+ "metadata": {
+ "description": "Optional. Auxiliary sku of Network Interface resource. Not all regions are enabled for Auxiliary Mode Nic."
+ }
+ },
+ "disableTcpStateTracking": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether to disable tcp state tracking. Subscription must be registered for the Microsoft.Network/AllowDisableTcpStateTracking feature before this property can be set to true."
+ }
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/networkInterfaceIPConfigurationType"
+ },
+ "metadata": {
+ "description": "Required. A list of IPConfigurations of the network interface."
+ }
+ },
+ "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."
+ }
+ }
+ },
+ "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')]",
+ "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": {
+ "publicIp": {
+ "copy": {
+ "name": "publicIp",
+ "count": "[length(parameters('ipConfigurations'))]"
+ },
+ "condition": "[and(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), not(equals(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), null())))]",
+ "existing": true,
+ "type": "Microsoft.Network/publicIPAddresses",
+ "apiVersion": "2024-05-01",
+ "resourceGroup": "[split(coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), ''), '/')[4]]",
+ "name": "[last(split(coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), ''), '/'))]"
+ },
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-03-01",
+ "name": "[format('46d3xbcp.res.network-networkinterface.{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"
+ }
+ }
+ }
+ }
+ },
+ "networkInterface": {
+ "type": "Microsoft.Network/networkInterfaces",
+ "apiVersion": "2024-05-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "copy": [
+ {
+ "name": "ipConfigurations",
+ "count": "[length(parameters('ipConfigurations'))]",
+ "input": {
+ "name": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'name'), format('ipconfig0{0}', add(copyIndex('ipConfigurations'), 1)))]",
+ "properties": {
+ "primary": "[if(equals(copyIndex('ipConfigurations'), 0), true(), false())]",
+ "privateIPAllocationMethod": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAllocationMethod')]",
+ "privateIPAddress": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddress')]",
+ "publicIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), if(not(equals(tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), null())), createObject('id', tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId')), null()), null())]",
+ "subnet": {
+ "id": "[parameters('ipConfigurations')[copyIndex('ipConfigurations')].subnetResourceId]"
+ },
+ "loadBalancerBackendAddressPools": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerBackendAddressPools')]",
+ "applicationSecurityGroups": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationSecurityGroups')]",
+ "applicationGatewayBackendAddressPools": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationGatewayBackendAddressPools')]",
+ "gatewayLoadBalancer": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'gatewayLoadBalancer')]",
+ "loadBalancerInboundNatRules": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerInboundNatRules')]",
+ "privateIPAddressVersion": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddressVersion')]",
+ "virtualNetworkTaps": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'virtualNetworkTaps')]"
+ }
+ }
+ }
+ ],
+ "auxiliaryMode": "[parameters('auxiliaryMode')]",
+ "auxiliarySku": "[parameters('auxiliarySku')]",
+ "disableTcpStateTracking": "[parameters('disableTcpStateTracking')]",
+ "dnsSettings": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', parameters('dnsServers')), null())]",
+ "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]",
+ "enableIPForwarding": "[parameters('enableIPForwarding')]",
+ "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]"
+ }
+ },
+ "networkInterface_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/networkInterfaces/{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": [
+ "networkInterface"
+ ]
+ },
+ "networkInterface_diagnosticSettings": {
+ "copy": {
+ "name": "networkInterface_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.Network/networkInterfaces/{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": [
+ "networkInterface"
+ ]
+ },
+ "networkInterface_roleAssignments": {
+ "copy": {
+ "name": "networkInterface_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkInterfaces', 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": [
+ "networkInterface"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed resource."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed resource."
+ },
+ "value": "[resourceId('Microsoft.Network/networkInterfaces', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group of the deployed resource."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('networkInterface', '2024-05-01', 'full').location]"
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/networkInterfaceIPConfigurationOutputType"
+ },
+ "metadata": {
+ "description": "The list of IP configurations of the network interface."
+ },
+ "copy": {
+ "count": "[length(parameters('ipConfigurations'))]",
+ "input": {
+ "name": "[reference('networkInterface').ipConfigurations[copyIndex()].name]",
+ "privateIP": "[coalesce(tryGet(reference('networkInterface').ipConfigurations[copyIndex()].properties, 'privateIPAddress'), '')]",
+ "publicIP": "[if(and(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), not(equals(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), null()))), coalesce(reference(format('publicIp[{0}]', copyIndex())).ipAddress, ''), '')]"
+ }
+ }
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "networkInterface_publicIPAddresses"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the network interface."
+ },
+ "value": "[reference('networkInterface').outputs.name.value]"
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/networkInterfaceIPConfigurationOutputType"
+ },
+ "metadata": {
+ "description": "The list of IP configurations of the network interface."
+ },
+ "value": "[reference('networkInterface').outputs.ipConfigurations.value]"
+ }
+ }
+ }
+ }
+ },
+ "vm_domainJoinExtension": {
+ "condition": "[and(contains(parameters('extensionDomainJoinConfig'), 'enabled'), parameters('extensionDomainJoinConfig').enabled)]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-DomainJoin', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'name'), 'DomainJoin')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Compute"
+ },
+ "type": {
+ "value": "JsonADDomainExtension"
+ },
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'typeHandlerVersion'), '1.3')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "settings": {
+ "value": "[parameters('extensionDomainJoinConfig').settings]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'tags'), parameters('tags'))]"
+ },
+ "protectedSettings": {
+ "value": {
+ "Password": "[parameters('extensionDomainJoinPassword')]"
+ }
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm"
+ ]
+ },
+ "vm_aadJoinExtension": {
+ "condition": "[parameters('extensionAadJoinConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-AADLogin', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'name'), 'AADLogin')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Azure.ActiveDirectory"
+ },
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AADLoginForWindows'), createObject('value', 'AADSSHLoginforLinux'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '2.0', '1.0'))]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "settings": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'settings'), createObject())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_domainJoinExtension"
+ ]
+ },
+ "vm_microsoftAntiMalwareExtension": {
+ "condition": "[parameters('extensionAntiMalwareConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-MicrosoftAntiMalware', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'name'), 'MicrosoftAntiMalware')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Azure.Security"
+ },
+ "type": {
+ "value": "IaaSAntimalware"
+ },
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'typeHandlerVersion'), '1.3')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "settings": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'settings'), createObject('AntimalwareEnabled', 'true', 'Exclusions', createObject(), 'RealtimeProtectionEnabled', 'true', 'ScheduledScanSettings', createObject('day', '7', 'isEnabled', 'true', 'scanType', 'Quick', 'time', '120')))]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_aadJoinExtension"
+ ]
+ },
+ "vm_azureMonitorAgentExtension": {
+ "condition": "[parameters('extensionMonitoringAgentConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-AzureMonitorAgent', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'name'), 'AzureMonitorAgent')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Azure.Monitor"
+ },
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureMonitorWindowsAgent'), createObject('value', 'AzureMonitorLinuxAgent'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '1.22', '1.29'))]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_microsoftAntiMalwareExtension"
+ ]
+ },
+ "vm_dependencyAgentExtension": {
+ "condition": "[parameters('extensionDependencyAgentConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-DependencyAgent', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'name'), 'DependencyAgent')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Azure.Monitoring.DependencyAgent"
+ },
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'DependencyAgentWindows'), createObject('value', 'DependencyAgentLinux'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), '9.10')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'enableAutomaticUpgrade'), true())]"
+ },
+ "settings": {
+ "value": {
+ "enableAMA": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'enableAMA'), true())]"
+ }
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_azureMonitorAgentExtension"
+ ]
+ },
+ "vm_networkWatcherAgentExtension": {
+ "condition": "[parameters('extensionNetworkWatcherAgentConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-NetworkWatcherAgent', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'name'), 'NetworkWatcherAgent')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Azure.NetworkWatcher"
+ },
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'NetworkWatcherAgentWindows'), createObject('value', 'NetworkWatcherAgentLinux'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'typeHandlerVersion'), '1.4')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_dependencyAgentExtension"
+ ]
+ },
+ "vm_desiredStateConfigurationExtension": {
+ "condition": "[parameters('extensionDSCConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-DesiredStateConfiguration', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'name'), 'DesiredStateConfiguration')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Powershell"
+ },
+ "type": {
+ "value": "DSC"
+ },
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'typeHandlerVersion'), '2.77')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "settings": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'settings'), createObject())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'tags'), parameters('tags'))]"
+ },
+ "protectedSettings": {
+ "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'protectedSettings'), 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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_networkWatcherAgentExtension"
+ ]
+ },
+ "vm_customScriptExtension": {
+ "condition": "[parameters('extensionCustomScriptConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-CustomScriptExtension', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'name'), 'CustomScriptExtension')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'Microsoft.Compute'), createObject('value', 'Microsoft.Azure.Extensions'))]",
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'CustomScriptExtension'), createObject('value', 'CustomScript'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '1.10', '2.1'))]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "settings": {
+ "value": {
+ "copy": [
+ {
+ "name": "fileUris",
+ "count": "[length(parameters('extensionCustomScriptConfig').fileData)]",
+ "input": "[if(contains(parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')], 'storageAccountId'), format('{0}?{1}', parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')].uri, listAccountSas(parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')].storageAccountId, '2019-04-01', variables('accountSasProperties')).accountSasToken), parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')].uri)]"
+ }
+ ]
+ }
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'tags'), parameters('tags'))]"
+ },
+ "protectedSettings": {
+ "value": "[parameters('extensionCustomScriptProtectedSetting')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_desiredStateConfigurationExtension"
+ ]
+ },
+ "vm_azureDiskEncryptionExtension": {
+ "condition": "[parameters('extensionAzureDiskEncryptionConfig').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-AzureDiskEncryption', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'name'), 'AzureDiskEncryption')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.Azure.Security"
+ },
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureDiskEncryption'), createObject('value', 'AzureDiskEncryptionForLinux'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '2.2', '1.1'))]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "forceUpdateTag": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'forceUpdateTag'), '1.0')]"
+ },
+ "settings": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'settings'), createObject())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_customScriptExtension"
+ ]
+ },
+ "vm_nvidiaGpuDriverWindowsExtension": {
+ "condition": "[parameters('extensionNvidiaGpuDriverWindows').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-NvidiaGpuDriverWindows', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'name'), 'NvidiaGpuDriverWindows')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.HpcCompute"
+ },
+ "type": {
+ "value": "NvidiaGpuDriverWindows"
+ },
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'typeHandlerVersion'), '1.4')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'supressFailures'), false())]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_azureDiskEncryptionExtension"
+ ]
+ },
+ "vm_hostPoolRegistrationExtension": {
+ "condition": "[parameters('extensionHostPoolRegistration').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-HostPoolRegistration', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'name'), 'HostPoolRegistration')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.PowerShell"
+ },
+ "type": {
+ "value": "DSC"
+ },
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'typeHandlerVersion'), '2.77')]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'enableAutomaticUpgrade'), false())]"
+ },
+ "settings": {
+ "value": {
+ "modulesUrl": "[parameters('extensionHostPoolRegistration').modulesUrl]",
+ "configurationFunction": "[parameters('extensionHostPoolRegistration').configurationFunction]",
+ "properties": {
+ "hostPoolName": "[parameters('extensionHostPoolRegistration').hostPoolName]",
+ "registrationInfoToken": "[parameters('extensionHostPoolRegistration').registrationInfoToken]",
+ "aadJoin": true
+ },
+ "supressFailures": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'supressFailures'), false())]"
+ }
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_nvidiaGpuDriverWindowsExtension"
+ ]
+ },
+ "vm_azureGuestConfigurationExtension": {
+ "condition": "[parameters('extensionGuestConfigurationExtension').enabled]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-GuestConfiguration', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "virtualMachineName": {
+ "value": "[parameters('name')]"
+ },
+ "name": "[if(coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'name'), equals(parameters('osType'), 'Windows')), createObject('value', 'AzurePolicyforWindows'), createObject('value', 'AzurePolicyforLinux'))]",
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "publisher": {
+ "value": "Microsoft.GuestConfiguration"
+ },
+ "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'ConfigurationforWindows'), createObject('value', 'ConfigurationForLinux'))]",
+ "typeHandlerVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '1.0', '1.0'))]"
+ },
+ "autoUpgradeMinorVersion": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'autoUpgradeMinorVersion'), true())]"
+ },
+ "enableAutomaticUpgrade": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'enableAutomaticUpgrade'), true())]"
+ },
+ "forceUpdateTag": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'forceUpdateTag'), '1.0')]"
+ },
+ "settings": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'settings'), createObject())]"
+ },
+ "supressFailures": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'supressFailures'), false())]"
+ },
+ "protectedSettings": {
+ "value": "[parameters('extensionGuestConfigurationExtensionProtectedSettings')]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), '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.36.177.2456",
+ "templateHash": "3746146591166938391"
+ },
+ "name": "Virtual Machine Extensions",
+ "description": "This module deploys a Virtual Machine Extension."
+ },
+ "parameters": {
+ "virtualMachineName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the virtual machine extension."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The location the extension is deployed to."
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the extension handler publisher."
+ }
+ },
+ "type": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"."
+ }
+ },
+ "typeHandlerVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Specifies the version of the script handler."
+ }
+ },
+ "autoUpgradeMinorVersion": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true."
+ }
+ },
+ "forceUpdateTag": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed."
+ }
+ },
+ "settings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific settings."
+ }
+ },
+ "protectedSettings": {
+ "type": "secureObject",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Any object that contains the extension specific protected settings."
+ }
+ },
+ "supressFailures": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false."
+ }
+ },
+ "enableAutomaticUpgrade": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "virtualMachine": {
+ "existing": true,
+ "type": "Microsoft.Compute/virtualMachines",
+ "apiVersion": "2022-11-01",
+ "name": "[parameters('virtualMachineName')]"
+ },
+ "extension": {
+ "type": "Microsoft.Compute/virtualMachines/extensions",
+ "apiVersion": "2022-11-01",
+ "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "publisher": "[parameters('publisher')]",
+ "type": "[parameters('type')]",
+ "typeHandlerVersion": "[parameters('typeHandlerVersion')]",
+ "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]",
+ "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]",
+ "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]",
+ "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]",
+ "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]",
+ "suppressFailures": "[parameters('supressFailures')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the extension."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the extension."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the extension was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('extension', '2022-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_hostPoolRegistrationExtension"
+ ]
+ },
+ "vm_backup": {
+ "condition": "[not(empty(parameters('backupVaultName')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-VM-Backup', uniqueString(deployment().name, parameters('location')))]",
+ "resourceGroup": "[parameters('backupVaultResourceGroup')]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[format('vm;iaasvmcontainerv2;{0};{1}', resourceGroup().name, parameters('name'))]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "policyId": {
+ "value": "[resourceId(parameters('backupVaultResourceGroup'), 'Microsoft.RecoveryServices/vaults/backupPolicies', parameters('backupVaultName'), parameters('backupPolicyName'))]"
+ },
+ "protectedItemType": {
+ "value": "Microsoft.Compute/virtualMachines"
+ },
+ "protectionContainerName": {
+ "value": "[format('iaasvmcontainer;iaasvmcontainerv2;{0};{1}', resourceGroup().name, parameters('name'))]"
+ },
+ "recoveryVaultName": {
+ "value": "[parameters('backupVaultName')]"
+ },
+ "sourceResourceId": {
+ "value": "[resourceId('Microsoft.Compute/virtualMachines', 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.36.177.2456",
+ "templateHash": "4883720476465660475"
+ },
+ "name": "Recovery Service Vaults Protection Container Protected Item",
+ "description": "This module deploys a Recovery Services Vault Protection Container Protected Item."
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the resource."
+ }
+ },
+ "protectionContainerName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment."
+ }
+ },
+ "recoveryVaultName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "protectedItemType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureFileShareProtectedItem",
+ "AzureVmWorkloadSAPAseDatabase",
+ "AzureVmWorkloadSAPHanaDatabase",
+ "AzureVmWorkloadSQLDatabase",
+ "DPMProtectedItem",
+ "GenericProtectedItem",
+ "MabFileFolderProtectedItem",
+ "Microsoft.ClassicCompute/virtualMachines",
+ "Microsoft.Compute/virtualMachines",
+ "Microsoft.Sql/servers/databases"
+ ],
+ "metadata": {
+ "description": "Required. The backup item type."
+ }
+ },
+ "policyId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. ID of the backup policy with which this item is backed up."
+ }
+ },
+ "sourceResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the resource to back up."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
+ "apiVersion": "2023-01-01",
+ "name": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "properties": {
+ "protectedItemType": "[parameters('protectedItemType')]",
+ "policyId": "[parameters('policyId')]",
+ "sourceResourceId": "[parameters('sourceResourceId')]"
+ }
+ }
+ ],
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Resource Group the protected item was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the protected item."
+ },
+ "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems', split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[2], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[3])]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The Name of the protected item."
+ },
+ "value": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vm",
+ "vm_azureGuestConfigurationExtension"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the VM."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the VM."
+ },
+ "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the VM 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('vm', '2024-07-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('vm', '2024-07-01', 'full').location]"
+ },
+ "nicConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/nicConfigurationOutputType"
+ },
+ "metadata": {
+ "description": "The list of NIC configurations of the virtual machine."
+ },
+ "copy": {
+ "count": "[length(parameters('nicConfigurations'))]",
+ "input": {
+ "name": "[reference(format('vm_nic[{0}]', copyIndex())).outputs.name.value]",
+ "ipConfigurations": "[reference(format('vm_nic[{0}]', copyIndex())).outputs.ipConfigurations.value]"
+ }
+ }
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace",
+ "maintenanceConfiguration",
+ "proximityPlacementGroup",
+ "virtualNetwork",
+ "windowsVmDataCollectionRules"
+ ]
+ },
+ "avmPrivateDnsZones": {
+ "copy": {
+ "name": "avmPrivateDnsZones",
+ "count": "[length(variables('privateDnsZones'))]",
+ "mode": "serial",
+ "batchSize": 5
+ },
+ "condition": "[and(parameters('enablePrivateNetworking'), or(not(variables('useExistingAiFoundryAiProject')), not(contains(variables('aiRelatedDnsZoneIndices'), copyIndex()))))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[format('avm.res.network.private-dns-zone.{0}', if(contains(variables('privateDnsZones')[copyIndex()], 'azurecontainerapps.io'), 'containerappenv', split(variables('privateDnsZones')[copyIndex()], '.')[1]))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('privateDnsZones')[copyIndex()]]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "virtualNetworkLinks": {
+ "value": [
+ {
+ "name": "[take(format('vnetlink-{0}-{1}', variables('virtualNetworkResourceName'), split(variables('privateDnsZones')[copyIndex()], '.')[1]), 80)]",
+ "virtualNetworkResourceId": "[reference('virtualNetwork').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": "4533956061065498344"
+ },
+ "name": "Private DNS Zones",
+ "description": "This module deploys a Private DNS zone."
+ },
+ "definitions": {
+ "aType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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,
+ "description": "The type for the A record."
+ }
+ },
+ "aaaaType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the AAAA record."
+ }
+ },
+ "cnameType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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_export!": true,
+ "description": "The type for the CNAME record."
+ }
+ },
+ "mxType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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_export!": true,
+ "description": "The type for the MX record."
+ }
+ },
+ "ptrType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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": {
+ "__bicep_export!": true,
+ "description": "The type for the PTR record."
+ }
+ },
+ "soaType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the SOA record."
+ }
+ },
+ "srvType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the SRV record."
+ }
+ },
+ "txtType": {
+ "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": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "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_export!": true,
+ "description": "The type for the TXT record."
+ }
+ },
+ "virtualNetworkLinkType": {
+ "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": {
+ "__bicep_export!": true,
+ "description": "The type for the virtual network link."
+ }
+ },
+ "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. Private DNS zone name."
+ }
+ },
+ "a": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/aType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of A records."
+ }
+ },
+ "aaaa": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/aaaaType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of AAAA records."
+ }
+ },
+ "cname": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/cnameType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of CNAME records."
+ }
+ },
+ "mx": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/mxType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of MX records."
+ }
+ },
+ "ptr": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ptrType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of PTR records."
+ }
+ },
+ "soa": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/soaType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of SOA records."
+ }
+ },
+ "srv": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/srvType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of SRV records."
+ }
+ },
+ "txt": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/txtType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of TXT records."
+ }
+ },
+ "virtualNetworkLinks": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/virtualNetworkLinkType"
+ },
+ "nullable": 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."
+ }
+ },
+ "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,
+ "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",
+ "nullable": true,
+ "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.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": {
+ "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.34.44.8038",
+ "templateHash": "18243374258187942664"
+ },
+ "name": "Private DNS Zone A record",
+ "description": "This module deploys a Private DNS Zone A record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "7322684246075092047"
+ },
+ "name": "Private DNS Zone AAAA record",
+ "description": "This module deploys a Private DNS Zone AAAA record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "5264706240021075859"
+ },
+ "name": "Private DNS Zone CNAME record",
+ "description": "This module deploys a Private DNS Zone CNAME record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "13758189936483275969"
+ },
+ "name": "Private DNS Zone MX record",
+ "description": "This module deploys a Private DNS Zone MX record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "11955164584650609753"
+ },
+ "name": "Private DNS Zone PTR record",
+ "description": "This module deploys a Private DNS Zone PTR record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "14626715835033259725"
+ },
+ "name": "Private DNS Zone SOA record",
+ "description": "This module deploys a Private DNS Zone SOA record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "6510442308165042737"
+ },
+ "name": "Private DNS Zone SRV record",
+ "description": "This module deploys a Private DNS Zone SRV record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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.34.44.8038",
+ "templateHash": "170623042781622569"
+ },
+ "name": "Private DNS Zone TXT record",
+ "description": "This module deploys a Private DNS Zone TXT record."
+ },
+ "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": {
+ "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": {
+ "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')]",
+ "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-VNetLink-{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.34.44.8038",
+ "templateHash": "725891200086243555"
+ },
+ "name": "Private DNS Zone Virtual Network Link",
+ "description": "This module deploys a Private DNS Zone Virtual Network Link."
+ },
+ "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]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "virtualNetwork"
+ ]
+ },
+ "existingAiFoundryAiServicesDeployments": {
+ "condition": "[variables('useExistingAiFoundryAiProject')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('module.ai-services-model-deployments.{0}', variables('aiFoundryAiServicesResourceName')), 64)]",
+ "subscriptionId": "[variables('aiFoundryAiServicesSubscriptionId')]",
+ "resourceGroup": "[variables('aiFoundryAiServicesResourceGroupName')]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('aiFoundryAiServicesResourceName')]"
+ },
+ "deployments": {
+ "value": [
+ {
+ "name": "[variables('aiFoundryAiServicesModelDeployment').name]",
+ "model": {
+ "format": "[variables('aiFoundryAiServicesModelDeployment').format]",
+ "name": "[variables('aiFoundryAiServicesModelDeployment').name]",
+ "version": "[variables('aiFoundryAiServicesModelDeployment').version]"
+ },
+ "raiPolicyName": "[variables('aiFoundryAiServicesModelDeployment').raiPolicyName]",
+ "sku": {
+ "name": "[variables('aiFoundryAiServicesModelDeployment').sku.name]",
+ "capacity": "[variables('aiFoundryAiServicesModelDeployment').sku.capacity]"
+ }
+ },
+ {
+ "name": "[variables('aiFoundryAiServices4_1ModelDeployment').name]",
+ "model": {
+ "format": "[variables('aiFoundryAiServices4_1ModelDeployment').format]",
+ "name": "[variables('aiFoundryAiServices4_1ModelDeployment').name]",
+ "version": "[variables('aiFoundryAiServices4_1ModelDeployment').version]"
+ },
+ "raiPolicyName": "[variables('aiFoundryAiServices4_1ModelDeployment').raiPolicyName]",
+ "sku": {
+ "name": "[variables('aiFoundryAiServices4_1ModelDeployment').sku.name]",
+ "capacity": "[variables('aiFoundryAiServices4_1ModelDeployment').sku.capacity]"
+ }
+ },
+ {
+ "name": "[variables('aiFoundryAiServicesReasoningModelDeployment').name]",
+ "model": {
+ "format": "[variables('aiFoundryAiServicesReasoningModelDeployment').format]",
+ "name": "[variables('aiFoundryAiServicesReasoningModelDeployment').name]",
+ "version": "[variables('aiFoundryAiServicesReasoningModelDeployment').version]"
+ },
+ "raiPolicyName": "[variables('aiFoundryAiServicesReasoningModelDeployment').raiPolicyName]",
+ "sku": {
+ "name": "[variables('aiFoundryAiServicesReasoningModelDeployment').sku.name]",
+ "capacity": "[variables('aiFoundryAiServicesReasoningModelDeployment').sku.capacity]"
+ }
+ }
+ ]
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "roleDefinitionIdOrName": "53ca6127-db72-4b80-b1b0-d745d6d5456d",
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "roleDefinitionIdOrName": "64702f94-c441-49e6-a78b-ef80e0188fee",
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "roleDefinitionIdOrName": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd",
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal"
+ }
+ ]
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.41.2.15936",
+ "templateHash": "8365054813170845685"
+ }
+ },
+ "definitions": {
+ "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": {
+ "description": "The type for a cognitive services account deployment.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/cognitive-services/account:0.13.2"
+ }
+ }
+ },
+ "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 Cognitive Services account."
+ }
+ },
+ "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."
+ }
+ },
+ "deployments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/deploymentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of deployments about cognitive service accounts to create."
+ }
+ },
+ "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": {
+ "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": {
+ "cognitiveService": {
+ "existing": true,
+ "type": "Microsoft.CognitiveServices/accounts",
+ "apiVersion": "2025-06-01",
+ "name": "[parameters('name')]"
+ },
+ "cognitiveService_deployments": {
+ "copy": {
+ "name": "cognitiveService_deployments",
+ "count": "[length(coalesce(parameters('deployments'), createArray()))]",
+ "mode": "serial",
+ "batchSize": 1
+ },
+ "type": "Microsoft.CognitiveServices/accounts/deployments",
+ "apiVersion": "2024-10-01",
+ "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')))]"
+ },
+ "cognitiveService_roleAssignments": {
+ "copy": {
+ "name": "cognitiveService_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[resourceId('Microsoft.CognitiveServices/accounts', 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": [
+ "userAssignedIdentity"
+ ]
+ },
+ "aiFoundryAiServices": {
+ "condition": "[not(variables('useExistingAiFoundryAiProject'))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.cognitive-services.account.{0}', variables('aiFoundryAiServicesResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('aiFoundryAiServicesResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('azureAiServiceLocation')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "sku": {
+ "value": "S0"
+ },
+ "kind": {
+ "value": "AIServices"
+ },
+ "disableLocalAuth": {
+ "value": true
+ },
+ "allowProjectManagement": {
+ "value": true
+ },
+ "customSubDomainName": {
+ "value": "[variables('aiFoundryAiServicesResourceName')]"
+ },
+ "apiProperties": {
+ "value": {}
+ },
+ "deployments": {
+ "value": [
+ {
+ "name": "[variables('aiFoundryAiServicesModelDeployment').name]",
+ "model": {
+ "format": "[variables('aiFoundryAiServicesModelDeployment').format]",
+ "name": "[variables('aiFoundryAiServicesModelDeployment').name]",
+ "version": "[variables('aiFoundryAiServicesModelDeployment').version]"
+ },
+ "raiPolicyName": "[variables('aiFoundryAiServicesModelDeployment').raiPolicyName]",
+ "sku": {
+ "name": "[variables('aiFoundryAiServicesModelDeployment').sku.name]",
+ "capacity": "[variables('aiFoundryAiServicesModelDeployment').sku.capacity]"
+ }
+ },
+ {
+ "name": "[variables('aiFoundryAiServices4_1ModelDeployment').name]",
+ "model": {
+ "format": "[variables('aiFoundryAiServices4_1ModelDeployment').format]",
+ "name": "[variables('aiFoundryAiServices4_1ModelDeployment').name]",
+ "version": "[variables('aiFoundryAiServices4_1ModelDeployment').version]"
+ },
+ "raiPolicyName": "[variables('aiFoundryAiServices4_1ModelDeployment').raiPolicyName]",
+ "sku": {
+ "name": "[variables('aiFoundryAiServices4_1ModelDeployment').sku.name]",
+ "capacity": "[variables('aiFoundryAiServices4_1ModelDeployment').sku.capacity]"
+ }
+ },
+ {
+ "name": "[variables('aiFoundryAiServicesReasoningModelDeployment').name]",
+ "model": {
+ "format": "[variables('aiFoundryAiServicesReasoningModelDeployment').format]",
+ "name": "[variables('aiFoundryAiServicesReasoningModelDeployment').name]",
+ "version": "[variables('aiFoundryAiServicesReasoningModelDeployment').version]"
+ },
+ "raiPolicyName": "[variables('aiFoundryAiServicesReasoningModelDeployment').raiPolicyName]",
+ "sku": {
+ "name": "[variables('aiFoundryAiServicesReasoningModelDeployment').sku.name]",
+ "capacity": "[variables('aiFoundryAiServicesReasoningModelDeployment').sku.capacity]"
+ }
+ }
+ ]
+ },
+ "networkAcls": {
+ "value": {
+ "defaultAction": "Allow",
+ "virtualNetworkRules": [],
+ "ipRules": []
+ }
+ },
+ "managedIdentities": {
+ "value": {
+ "userAssignedResourceIds": [
+ "[reference('userAssignedIdentity').outputs.resourceId.value]"
+ ]
+ }
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "roleDefinitionIdOrName": "53ca6127-db72-4b80-b1b0-d745d6d5456d",
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "roleDefinitionIdOrName": "64702f94-c441-49e6-a78b-ef80e0188fee",
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "roleDefinitionIdOrName": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd",
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "roleDefinitionIdOrName": "53ca6127-db72-4b80-b1b0-d745d6d5456d",
+ "principalId": "[variables('deployingUserPrincipalId')]",
+ "principalType": "[variables('deployerPrincipalType')]"
+ },
+ {
+ "roleDefinitionIdOrName": "64702f94-c441-49e6-a78b-ef80e0188fee",
+ "principalId": "[variables('deployingUserPrincipalId')]",
+ "principalType": "[variables('deployerPrincipalType')]"
+ }
+ ]
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]",
+ "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]",
+ "privateEndpoints": {
+ "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.37.4.10188",
+ "templateHash": "9381727816193702843"
+ },
+ "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."
+ }
+ },
+ "commitmentPlanType": {
+ "type": "object",
+ "properties": {
+ "autoRenew": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Whether the plan should auto-renew at the end of the current commitment period."
+ }
+ },
+ "current": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. The number of committed instances (e.g., number of containers or cores)."
+ }
+ },
+ "tier": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The tier of the commitment plan (e.g., T1, T2)."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. The current commitment configuration."
+ }
+ },
+ "hostingModel": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The hosting model for the commitment plan. (e.g., DisconnectedContainer, ConnectedContainer, ProvisionedWeb, Web)."
+ }
+ },
+ "planType": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The plan type indicating which capability the plan applies to (e.g., NTTS, STT, CUSTOMSTT, ADDON)."
+ }
+ },
+ "commitmentPlanGuid": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The unique identifier of an existing commitment plan to update. Set to null to create a new plan."
+ }
+ },
+ "next": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. The number of committed instances for the next period."
+ }
+ },
+ "tier": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The tier for the next commitment period."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The configuration of the next commitment period, if scheduled."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a disconnected container commitment plan."
+ }
+ },
+ "networkInjectionType": {
+ "type": "object",
+ "properties": {
+ "scenario": {
+ "type": "string",
+ "allowedValues": [
+ "agent",
+ "none"
+ ],
+ "metadata": {
+ "description": "Required. The scenario for the network injection."
+ }
+ },
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Resource ID of the subnet on the Virtual Network on which to inject."
+ }
+ },
+ "useMicrosoftManagedNetwork": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Whether to use Microsoft Managed Network. Defaults to false."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "Type for network configuration in AI Foundry where virtual network injection occurs to secure scenarios like Agents entirely within a private network."
+ }
+ },
+ "_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.6.0"
+ }
+ }
+ },
+ "_2.lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "notes": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the notes of the lock."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a lock.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1"
+ }
+ }
+ },
+ "_2.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.6.1"
+ }
+ }
+ },
+ "_2.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.6.1"
+ }
+ }
+ },
+ "_2.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.6.1"
+ }
+ }
+ },
+ "_2.roleAssignmentType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated."
+ }
+ },
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a role assignment.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.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.6.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.6.0"
+ }
+ }
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "notes": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the notes of the lock."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a lock.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "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.6.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."
+ }
+ },
+ "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/_2.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/_2.privateEndpointCustomDnsConfigType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Custom DNS configurations."
+ }
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_2.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/_2.lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_2.roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-07-01#properties/tags"
+ },
+ "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.6.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.6.0"
+ }
+ }
+ },
+ "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.6.0"
+ }
+ }
+ }
+ },
+ "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",
+ "DC0"
+ ],
+ "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."
+ }
+ },
+ "networkInjections": {
+ "$ref": "#/definitions/networkInjectionType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies in AI Foundry where virtual network injection occurs to secure scenarios like Agents entirely within a private network."
+ }
+ },
+ "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",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.CognitiveServices/accounts@2025-04-01-preview#properties/properties/properties/userOwnedStorage"
+ },
+ "description": "Optional. The storage accounts for this resource."
+ },
+ "nullable": true
+ },
+ "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."
+ }
+ },
+ "commitmentPlans": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/commitmentPlanType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Commitment plans to deploy for the cognitive services 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)))))]"
+ }
+ ],
+ "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": "2024-11-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.13.2', '.', '-'), 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": "2024-11-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-06-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())]",
+ "networkInjections": "[if(not(empty(parameters('networkInjections'))), createArray(createObject('scenario', tryGet(parameters('networkInjections'), 'scenario'), 'subnetArmId', tryGet(parameters('networkInjections'), 'subnetResourceId'), 'useMicrosoftManagedNetwork', coalesce(tryGet(parameters('networkInjections'), 'useMicrosoftManagedNetwork'), false()))), 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": "[if(not(empty(parameters('userOwnedStorage'))), parameters('userOwnedStorage'), null())]",
+ "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-06-01",
+ "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": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]"
+ },
+ "dependsOn": [
+ "cognitiveService"
+ ]
+ },
+ "cognitiveService_commitmentPlans": {
+ "copy": {
+ "name": "cognitiveService_commitmentPlans",
+ "count": "[length(coalesce(parameters('commitmentPlans'), createArray()))]"
+ },
+ "type": "Microsoft.CognitiveServices/accounts/commitmentPlans",
+ "apiVersion": "2025-06-01",
+ "name": "[format('{0}/{1}', parameters('name'), format('{0}-{1}', coalesce(parameters('commitmentPlans'), createArray())[copyIndex()].hostingModel, coalesce(parameters('commitmentPlans'), createArray())[copyIndex()].planType))]",
+ "properties": "[coalesce(parameters('commitmentPlans'), createArray())[copyIndex()]]",
+ "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-06-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-06-01').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.37.4.10188",
+ "templateHash": "10828079590669389085"
+ }
+ },
+ "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": "2024-11-01",
+ "name": "[parameters('keyVaultName')]"
+ },
+ "secrets": {
+ "copy": {
+ "name": "secrets",
+ "count": "[length(parameters('secretsToSet'))]"
+ },
+ "type": "Microsoft.KeyVault/vaults/secrets",
+ "apiVersion": "2024-11-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-06-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('cognitiveService', '2025-06-01', '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]"
+ }
+ }
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace",
+ "userAssignedIdentity"
+ ]
+ },
+ "aiFoundryPrivateEndpoint": {
+ "condition": "[and(parameters('enablePrivateNetworking'), not(variables('useExistingAiFoundryAiProject')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('pep-{0}-deployment', variables('aiFoundryAiServicesResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[format('pep-{0}', variables('aiFoundryAiServicesResourceName'))]"
+ },
+ "customNetworkInterfaceName": {
+ "value": "[format('nic-{0}', variables('aiFoundryAiServicesResourceName'))]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "privateLinkServiceConnections": {
+ "value": [
+ {
+ "name": "[format('pep-{0}-connection', variables('aiFoundryAiServicesResourceName'))]",
+ "properties": {
+ "privateLinkServiceId": "[reference('aiFoundryAiServices').outputs.resourceId.value]",
+ "groupIds": [
+ "account"
+ ]
+ }
+ }
+ ]
+ },
+ "privateDnsZoneGroup": {
+ "value": {
+ "privateDnsZoneGroupConfigs": [
+ {
+ "name": "ai-services-dns-zone-cognitiveservices",
+ "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)).outputs.resourceId.value]"
+ },
+ {
+ "name": "ai-services-dns-zone-openai",
+ "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)).outputs.resourceId.value]"
+ },
+ {
+ "name": "ai-services-dns-zone-aiservices",
+ "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)).outputs.resourceId.value]"
+ }
+ ]
+ }
+ },
+ "subnetResourceId": {
+ "value": "[reference('virtualNetwork').outputs.backendSubnetResourceId.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.30.23.60470",
+ "templateHash": "2541425927059591098"
+ },
+ "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",
+ "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
+ },
+ "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",
+ "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
+ },
+ "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": "[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.8.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.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": {
+ "$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]"
+ },
+ "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": [
+ "aiFoundryAiServices",
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]",
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]",
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]",
+ "virtualNetwork"
+ ]
+ },
+ "aiFoundryAiServicesProject": {
+ "condition": "[not(variables('useExistingAiFoundryAiProject'))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('module.ai-project.{0}', variables('aiFoundryAiProjectResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('aiFoundryAiProjectResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('azureAiServiceLocation')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "desc": {
+ "value": "[variables('aiFoundryAiProjectDescription')]"
+ },
+ "aiServicesName": {
+ "value": "[reference('aiFoundryAiServices').outputs.name.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.41.2.15936",
+ "templateHash": "5789718034225488560"
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the AI Services project."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Required. The location of the Project resource."
+ }
+ },
+ "desc": {
+ "type": "string",
+ "defaultValue": "[parameters('name')]",
+ "metadata": {
+ "description": "Optional. The description of the AI Foundry project to create. Defaults to the project name."
+ }
+ },
+ "aiServicesName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the existing Cognitive Services resource to create the AI Foundry project in."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Tags to be applied to the resources."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.CognitiveServices/accounts/projects",
+ "apiVersion": "2025-06-01",
+ "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('name'))]",
+ "tags": "[parameters('tags')]",
+ "location": "[parameters('location')]",
+ "identity": {
+ "type": "SystemAssigned"
+ },
+ "properties": {
+ "description": "[parameters('desc')]",
+ "displayName": "[parameters('name')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the AI project."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the AI project."
+ },
+ "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name'))]"
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Principal ID of the AI project managed identity."
+ },
+ "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name')), '2025-06-01', 'full').identity.principalId]"
+ },
+ "apiEndpoint": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. API endpoint for the AI project."
+ },
+ "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name')), '2025-06-01').endpoints['AI Foundry API']]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "aiFoundryAiServices",
+ "aiFoundryPrivateEndpoint"
+ ]
+ },
+ "cosmosDb": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.document-db.database-account.{0}', variables('cosmosDbResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('cosmosDbResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "sqlDatabases": {
+ "value": [
+ {
+ "name": "[variables('cosmosDbDatabaseName')]",
+ "containers": [
+ {
+ "name": "[variables('cosmosDbDatabaseMemoryContainerName')]",
+ "paths": [
+ "/session_id"
+ ],
+ "kind": "Hash",
+ "version": 2
+ }
+ ]
+ }
+ ]
+ },
+ "dataPlaneRoleDefinitions": {
+ "value": [
+ {
+ "roleName": "Cosmos DB SQL Data Contributor",
+ "dataActions": [
+ "Microsoft.DocumentDB/databaseAccounts/readMetadata",
+ "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*",
+ "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*"
+ ],
+ "assignments": [
+ {
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]"
+ },
+ {
+ "principalId": "[variables('deployingUserPrincipalId')]"
+ }
+ ]
+ }
+ ]
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]",
+ "networkRestrictions": {
+ "value": {
+ "networkAclBypass": "None",
+ "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]"
+ }
+ },
+ "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-{0}', variables('cosmosDbResourceName')), 'customNetworkInterfaceName', format('nic-{0}', variables('cosmosDbResourceName')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cosmosDb)).outputs.resourceId.value))), 'service', 'Sql', 'subnetResourceId', reference('virtualNetwork').outputs.backendSubnetResourceId.value))), createObject('value', createArray()))]",
+ "zoneRedundant": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]",
+ "capabilitiesToAdd": "[if(parameters('enableRedundancy'), createObject('value', null()), createObject('value', createArray('EnableServerless')))]",
+ "automaticFailover": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]",
+ "failoverLocations": "[if(parameters('enableRedundancy'), createObject('value', createArray(createObject('failoverPriority', 0, 'isZoneRedundant', true(), 'locationName', parameters('location')), createObject('failoverPriority', 1, 'isZoneRedundant', true(), 'locationName', variables('cosmosDbHaLocation')))), createObject('value', createArray(createObject('locationName', parameters('location'), 'failoverPriority', 0, 'isZoneRedundant', parameters('enableRedundancy')))))]"
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "8020152823352819436"
+ },
+ "name": "Azure Cosmos DB account",
+ "description": "This module deploys an Azure Cosmos DB account. The API used for the account is determined by the child resources that are deployed."
+ },
+ "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": "fully-qualified domain name (FQDN) that resolves to private endpoint IP address."
+ }
+ },
+ "ipAddresses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "metadata": {
+ "description": "A list of private IP addresses for 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."
+ }
+ },
+ "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. Flag to indicate whether or not this region is an AvailabilityZone region. Defaults to true."
+ }
+ },
+ "locationName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the region."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the failover location."
+ }
+ },
+ "dataPlaneRoleAssignmentType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The unique name of the role assignment."
+ }
+ },
+ "roleDefinitionId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier of the Azure Cosmos DB for NoSQL native role-based access control definition."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier for the associated Microsoft Entra ID principal to which access is being granted through this role-based access control assignment. The tenant ID for the principal is inferred using the tenant associated with the subscription."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an Azure Cosmos DB for NoSQL native role-based access control assignment."
+ }
+ },
+ "dataPlaneRoleDefinitionType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The unique identifier of the role-based access control definition."
+ }
+ },
+ "roleName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A user-friendly name for the role-based access control definition. This must be unique within the database account."
+ }
+ },
+ "dataActions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. An array of data actions that are allowed."
+ }
+ },
+ "assignableScopes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A set of fully-qualified scopes at or below which role-based access control assignments may be created using this definition. This setting allows application of this definition on the entire account or any underlying resource. This setting must have at least one element. Scopes higher than the account level are not enforceable as assignable scopes. Resources referenced in assignable scopes do not need to exist at creation. Defaults to the current account scope."
+ }
+ },
+ "assignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/sqlRoleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. An array of role-based access control assignments to be created for the definition."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an Azure Cosmos DB for NoSQL or Table native role-based access control definition."
+ }
+ },
+ "sqlDatabaseType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the 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. Defaults to 400."
+ }
+ },
+ "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 the value is not set, 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. Set of containers to deploy in the database."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an Azure Cosmos DB for NoSQL database."
+ }
+ },
+ "networkRestrictionType": {
+ "type": "object",
+ "properties": {
+ "ipRules": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A single IPv4 address or a single IPv4 address range in Classless Inter-Domain Routing (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. Specifies the network ACL bypass for Azure services. Default to \"None\"."
+ }
+ },
+ "publicNetworkAccess": {
+ "type": "string",
+ "allowedValues": [
+ "Disabled",
+ "Enabled"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Whether requests from the public network are allowed. Default to \"Disabled\"."
+ }
+ },
+ "virtualNetworkRules": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of a subnet."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of virtual network access control list (ACL) rules configured for the account."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the network restriction."
+ }
+ },
+ "_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"
+ }
+ }
+ },
+ "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": {
+ "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 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."
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "sqlRoleAssignmentType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name unique identifier of the SQL Role Assignment."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the SQL Role Assignments.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "sql-role-definition/main.bicep"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the account."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Defaults to the current resource group scope location. Location for all resources."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.DocumentDB/databaseAccounts@2024-11-15#properties/tags"
+ },
+ "description": "Optional. Tags for the resource."
+ },
+ "nullable": true
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "databaseAccountOfferType": {
+ "type": "string",
+ "defaultValue": "Standard",
+ "allowedValues": [
+ "Standard"
+ ],
+ "metadata": {
+ "description": "Optional. The offer type for the account. Defaults to \"Standard\"."
+ }
+ },
+ "failoverLocations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/failoverLocationType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The set of locations enabled for the account. Defaults to the location where the account is deployed."
+ }
+ },
+ "zoneRedundant": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Indicates whether the single-region account is zone redundant. Defaults to true. This property is ignored for multi-region accounts."
+ }
+ },
+ "defaultConsistencyLevel": {
+ "type": "string",
+ "defaultValue": "Session",
+ "allowedValues": [
+ "Eventual",
+ "ConsistentPrefix",
+ "Session",
+ "BoundedStaleness",
+ "Strong"
+ ],
+ "metadata": {
+ "description": "Optional. The default consistency level of the account. Defaults to \"Session\"."
+ }
+ },
+ "disableLocalAuthentication": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Opt-out of local authentication and ensure that only Microsoft Entra can be used exclusively for authentication. Defaults to true."
+ }
+ },
+ "enableAnalyticalStorage": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Flag to indicate whether to enable storage analytics. Defaults to false."
+ }
+ },
+ "automaticFailover": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable automatic failover for regions. Defaults to true."
+ }
+ },
+ "enableFreeTier": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Flag to indicate whether \"Free Tier\" is enabled. Defaults to false."
+ }
+ },
+ "enableMultipleWriteLocations": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enables the account to write in multiple locations. Periodic backup must be used if enabled. Defaults to false."
+ }
+ },
+ "disableKeyBasedMetadataWriteAccess": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Disable write operations on metadata resources (databases, containers, throughput) via account keys. Defaults to true."
+ }
+ },
+ "maxStalenessPrefix": {
+ "type": "int",
+ "defaultValue": 100000,
+ "minValue": 1,
+ "maxValue": 2147483647,
+ "metadata": {
+ "description": "Optional. The maximum stale requests. Required for \"BoundedStaleness\" consistency level. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000. Defaults to 100000."
+ }
+ },
+ "maxIntervalInSeconds": {
+ "type": "int",
+ "defaultValue": 300,
+ "minValue": 5,
+ "maxValue": 86400,
+ "metadata": {
+ "description": "Optional. The maximum lag time in minutes. Required for \"BoundedStaleness\" consistency level. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400. Defaults to 300."
+ }
+ },
+ "serverVersion": {
+ "type": "string",
+ "defaultValue": "4.2",
+ "allowedValues": [
+ "3.2",
+ "3.6",
+ "4.0",
+ "4.2",
+ "5.0",
+ "6.0",
+ "7.0"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the MongoDB server version to use if using Azure Cosmos DB for MongoDB RU. Defaults to \"4.2\"."
+ }
+ },
+ "sqlDatabases": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/sqlDatabaseType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configuration for databases when using Azure Cosmos DB for NoSQL."
+ }
+ },
+ "mongodbDatabases": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configuration for databases when using Azure Cosmos DB for MongoDB RU."
+ }
+ },
+ "gremlinDatabases": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configuration for databases when using Azure Cosmos DB for Apache Gremlin."
+ }
+ },
+ "tables": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configuration for databases when using Azure Cosmos DB for Table."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "totalThroughputLimit": {
+ "type": "int",
+ "defaultValue": -1,
+ "metadata": {
+ "description": "Optional. The total throughput limit imposed on this account in request units per second (RU/s). Default to unlimited throughput."
+ }
+ },
+ "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. An array of control plane Azure role-based access control assignments."
+ }
+ },
+ "dataPlaneRoleDefinitions": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/dataPlaneRoleDefinitionType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configurations for Azure Cosmos DB for NoSQL native role-based access control definitions. Allows the creations of custom role definitions."
+ }
+ },
+ "dataPlaneRoleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/dataPlaneRoleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configurations for Azure Cosmos DB for NoSQL native role-based access control assignments."
+ }
+ },
+ "diagnosticSettings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/diagnosticSettingFullType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The diagnostic settings for the service."
+ }
+ },
+ "capabilitiesToAdd": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "allowedValues": [
+ "EnableCassandra",
+ "EnableTable",
+ "EnableGremlin",
+ "EnableMongo",
+ "DisableRateLimitingResponses",
+ "EnableServerless",
+ "EnableNoSQLVectorSearch",
+ "EnableNoSQLFullTextSearch",
+ "EnableMaterializedViews",
+ "DeleteAllItemsByPartitionKey"
+ ],
+ "metadata": {
+ "description": "Optional. A list of Azure Cosmos DB specific capabilities for the account."
+ }
+ },
+ "backupPolicyType": {
+ "type": "string",
+ "defaultValue": "Continuous",
+ "allowedValues": [
+ "Periodic",
+ "Continuous"
+ ],
+ "metadata": {
+ "description": "Optional. Configures the backup mode. Periodic backup must be used if multiple write locations are used. Defaults to \"Continuous\"."
+ }
+ },
+ "backupPolicyContinuousTier": {
+ "type": "string",
+ "defaultValue": "Continuous30Days",
+ "allowedValues": [
+ "Continuous30Days",
+ "Continuous7Days"
+ ],
+ "metadata": {
+ "description": "Optional. Configuration values to specify the retention period for continuous mode backup. Default to \"Continuous30Days\"."
+ }
+ },
+ "backupIntervalInMinutes": {
+ "type": "int",
+ "defaultValue": 240,
+ "minValue": 60,
+ "maxValue": 1440,
+ "metadata": {
+ "description": "Optional. An integer representing the interval in minutes between two backups. This setting only applies to the periodic backup type. Defaults to 240."
+ }
+ },
+ "backupRetentionIntervalInHours": {
+ "type": "int",
+ "defaultValue": 8,
+ "minValue": 2,
+ "maxValue": 720,
+ "metadata": {
+ "description": "Optional. An integer representing the time (in hours) that each backup is retained. This setting only applies to the periodic backup type. Defaults to 8."
+ }
+ },
+ "backupStorageRedundancy": {
+ "type": "string",
+ "defaultValue": "Local",
+ "allowedValues": [
+ "Geo",
+ "Local",
+ "Zone"
+ ],
+ "metadata": {
+ "description": "Optional. Setting that indicates the type of backup residency. This setting only applies to the periodic backup type. Defaults to \"Local\"."
+ }
+ },
+ "privateEndpoints": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/privateEndpointMultiServiceType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Configuration details for private endpoints. For security reasons, it is advised to use private endpoints whenever possible."
+ }
+ },
+ "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. Setting that indicates the minimum allowed TLS version. Azure Cosmos DB for MongoDB RU and Apache Cassandra only work with TLS 1.2 or later. Defaults to \"Tls12\" (TLS 1.2)."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "formattedRoleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]",
+ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInControlPlaneRoleNames'), 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())]",
+ "builtInControlPlaneRoleNames": {
+ "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": "2024-07-01",
+ "name": "[format('46d3xbcp.res.documentdb-databaseaccount.{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"
+ }
+ }
+ }
+ }
+ },
+ "databaseAccount": {
+ "type": "Microsoft.DocumentDB/databaseAccounts",
+ "apiVersion": "2024-11-15",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
+ "kind": "[if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB')]",
+ "properties": "[shallowMerge(createArray(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType'), 'backupPolicy', shallowMerge(createArray(createObject('type', parameters('backupPolicyType')), if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject()), if(equals(parameters('backupPolicyType'), 'Periodic'), createObject('periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))), createObject()))), 'capabilities', map(coalesce(parameters('capabilitiesToAdd'), createArray()), lambda('capability', createObject('name', lambdaVariables('capability')))), 'minimalTlsVersion', parameters('minimumTlsVersion'), 'capacity', createObject('totalThroughputLimit', parameters('totalThroughputLimit')), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Disabled')), if(or(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('tables')))), createObject('consistencyPolicy', shallowMerge(createArray(createObject('defaultConsistencyLevel', parameters('defaultConsistencyLevel')), if(equals(parameters('defaultConsistencyLevel'), 'BoundedStaleness'), createObject('maxStalenessPrefix', parameters('maxStalenessPrefix'), 'maxIntervalInSeconds', parameters('maxIntervalInSeconds')), createObject()))), 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(not(empty(parameters('failoverLocations'))), map(parameters('failoverLocations'), lambda('failoverLocation', createObject('failoverPriority', lambdaVariables('failoverLocation').failoverPriority, 'locationName', lambdaVariables('failoverLocation').locationName, 'isZoneRedundant', coalesce(tryGet(lambdaVariables('failoverLocation'), 'isZoneRedundant'), true())))), createArray(createObject('failoverPriority', 0, 'locationName', parameters('location'), 'isZoneRedundant', parameters('zoneRedundant')))), 'ipRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray()), lambda('ipRule', createObject('ipAddressOrRange', lambdaVariables('ipRule')))), 'virtualNetworkRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray()), lambda('rule', createObject('id', lambdaVariables('rule').subnetResourceId, 'ignoreMissingVNetServiceEndpoint', false()))), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'None'), 'isVirtualNetworkFilterEnabled', or(not(empty(tryGet(parameters('networkRestrictions'), 'ipRules'))), not(empty(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules')))), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(or(not(empty(parameters('mongodbDatabases'))), not(empty(parameters('gremlinDatabases')))), createObject('disableLocalAuth', false(), 'disableKeyBasedMetadataWriteAccess', false()), createObject('disableLocalAuth', parameters('disableLocalAuthentication'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess'))), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject())))]"
+ },
+ "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())]"
+ }
+ }
+ ],
+ "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(coalesce(parameters('sqlDatabases'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('sqlDatabases'), createArray())[copyIndex()].name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[coalesce(parameters('sqlDatabases'), createArray())[copyIndex()].name]"
+ },
+ "containers": {
+ "value": "[tryGet(coalesce(parameters('sqlDatabases'), createArray())[copyIndex()], 'containers')]"
+ },
+ "throughput": {
+ "value": "[tryGet(coalesce(parameters('sqlDatabases'), createArray())[copyIndex()], 'throughput')]"
+ },
+ "databaseAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "autoscaleSettingsMaxThroughput": {
+ "value": "[tryGet(coalesce(parameters('sqlDatabases'), createArray())[copyIndex()], 'autoscaleSettingsMaxThroughput')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "6801379641184405078"
+ },
+ "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"
+ },
+ "nullable": true,
+ "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": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "sqlDatabase": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases",
+ "apiVersion": "2024-11-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(coalesce(parameters('containers'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('name')), coalesce(parameters('containers'), createArray())[copyIndex()].name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('databaseAccountName')]"
+ },
+ "sqlDatabaseName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]"
+ },
+ "analyticalStorageTtl": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'analyticalStorageTtl')]"
+ },
+ "autoscaleSettingsMaxThroughput": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'autoscaleSettingsMaxThroughput')]"
+ },
+ "conflictResolutionPolicy": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'conflictResolutionPolicy')]"
+ },
+ "defaultTtl": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultTtl')]"
+ },
+ "indexingPolicy": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'indexingPolicy')]"
+ },
+ "kind": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'kind')]"
+ },
+ "version": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'version')]"
+ },
+ "paths": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'paths')]"
+ },
+ "throughput": "[if(and(or(not(equals(parameters('throughput'), null())), not(equals(parameters('autoscaleSettingsMaxThroughput'), null()))), equals(tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'throughput'), null())), createObject('value', -1), createObject('value', tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'throughput')))]",
+ "uniqueKeyPolicyKeys": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[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.35.1.17967",
+ "templateHash": "5467755913632158534"
+ },
+ "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": [
+ "sqlDatabase"
+ ]
+ }
+ },
+ "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]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "databaseAccount"
+ ]
+ },
+ "databaseAccount_sqlRoleDefinitions": {
+ "copy": {
+ "name": "databaseAccount_sqlRoleDefinitions",
+ "count": "[length(coalesce(parameters('dataPlaneRoleDefinitions'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-sqlrd-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'name')]"
+ },
+ "dataActions": {
+ "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'dataActions')]"
+ },
+ "roleName": {
+ "value": "[coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()].roleName]"
+ },
+ "assignableScopes": {
+ "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'assignableScopes')]"
+ },
+ "sqlRoleAssignments": {
+ "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'assignments')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "12119240119487993734"
+ },
+ "name": "DocumentDB Database Account SQL Role Definitions.",
+ "description": "This module deploys a SQL Role Definision in a CosmosDB Account."
+ },
+ "definitions": {
+ "sqlRoleAssignmentType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name unique identifier of the SQL Role Assignment."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the SQL Role Assignments."
+ }
+ }
+ },
+ "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",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The unique identifier of the Role Definition."
+ }
+ },
+ "roleName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A user-friendly name for the Role Definition. Must be unique for the database account."
+ }
+ },
+ "dataActions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. An array of data actions that are allowed."
+ }
+ },
+ "assignableScopes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A set of fully qualified Scopes at or below which Role Assignments may be created using this Role Definition. This will allow application of this Role Definition on the entire database account or any underlying Database / Collection. Must have at least one element. Scopes higher than Database account are not enforceable as assignable Scopes. Note that resources referenced in assignable Scopes need not exist. Defaults to the current account."
+ }
+ },
+ "sqlRoleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/sqlRoleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. An array of SQL Role Assignments to be created for the SQL Role Definition."
+ }
+ }
+ },
+ "resources": {
+ "databaseAccount": {
+ "existing": true,
+ "type": "Microsoft.DocumentDB/databaseAccounts",
+ "apiVersion": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "sqlRoleDefinition": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions",
+ "apiVersion": "2024-11-15",
+ "name": "[format('{0}/{1}', parameters('databaseAccountName'), coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')))]",
+ "properties": {
+ "assignableScopes": "[coalesce(parameters('assignableScopes'), createArray(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))))]",
+ "permissions": [
+ {
+ "dataActions": "[parameters('dataActions')]"
+ }
+ ],
+ "roleName": "[parameters('roleName')]",
+ "type": "CustomRole"
+ }
+ },
+ "databaseAccount_sqlRoleAssignments": {
+ "copy": {
+ "name": "databaseAccount_sqlRoleAssignments",
+ "count": "[length(coalesce(parameters('sqlRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-sqlra-{1}', uniqueString(deployment().name), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('databaseAccountName')]"
+ },
+ "roleDefinitionId": {
+ "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')))]"
+ },
+ "principalId": {
+ "value": "[coalesce(parameters('sqlRoleAssignments'), createArray())[copyIndex()].principalId]"
+ },
+ "name": {
+ "value": "[tryGet(coalesce(parameters('sqlRoleAssignments'), createArray())[copyIndex()], '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.35.1.17967",
+ "templateHash": "11941443499827753966"
+ },
+ "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",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name unique identifier of the SQL Role Assignment."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription."
+ }
+ },
+ "roleDefinitionId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier of the associated SQL Role Definition."
+ }
+ }
+ },
+ "resources": {
+ "databaseAccount": {
+ "existing": true,
+ "type": "Microsoft.DocumentDB/databaseAccounts",
+ "apiVersion": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "sqlRoleAssignment": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments",
+ "apiVersion": "2024-11-15",
+ "name": "[format('{0}/{1}', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]",
+ "properties": {
+ "principalId": "[parameters('principalId')]",
+ "roleDefinitionId": "[parameters('roleDefinitionId')]",
+ "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the SQL Role Assignment."
+ },
+ "value": "[coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))))]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the SQL Role Assignment."
+ },
+ "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the SQL Role Definition was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "sqlRoleDefinition"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the SQL Role Definition."
+ },
+ "value": "[coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the SQL Role Definition."
+ },
+ "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the SQL Role Definition was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "roleName": {
+ "type": "string",
+ "metadata": {
+ "description": "The role name of the SQL Role Definition."
+ },
+ "value": "[reference('sqlRoleDefinition').roleName]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "databaseAccount"
+ ]
+ },
+ "databaseAccount_sqlRoleAssignments": {
+ "copy": {
+ "name": "databaseAccount_sqlRoleAssignments",
+ "count": "[length(coalesce(parameters('dataPlaneRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-sqlra-{1}', uniqueString(deployment().name), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "roleDefinitionId": {
+ "value": "[coalesce(parameters('dataPlaneRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]"
+ },
+ "principalId": {
+ "value": "[coalesce(parameters('dataPlaneRoleAssignments'), createArray())[copyIndex()].principalId]"
+ },
+ "name": {
+ "value": "[tryGet(coalesce(parameters('dataPlaneRoleAssignments'), createArray())[copyIndex()], '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.35.1.17967",
+ "templateHash": "11941443499827753966"
+ },
+ "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",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name unique identifier of the SQL Role Assignment."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription."
+ }
+ },
+ "roleDefinitionId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The unique identifier of the associated SQL Role Definition."
+ }
+ }
+ },
+ "resources": {
+ "databaseAccount": {
+ "existing": true,
+ "type": "Microsoft.DocumentDB/databaseAccounts",
+ "apiVersion": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "sqlRoleAssignment": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments",
+ "apiVersion": "2024-11-15",
+ "name": "[format('{0}/{1}', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]",
+ "properties": {
+ "principalId": "[parameters('principalId')]",
+ "roleDefinitionId": "[parameters('roleDefinitionId')]",
+ "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the SQL Role Assignment."
+ },
+ "value": "[coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))))]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the SQL Role Assignment."
+ },
+ "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the SQL Role Definition was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "databaseAccount"
+ ]
+ },
+ "databaseAccount_mongodbDatabases": {
+ "copy": {
+ "name": "databaseAccount_mongodbDatabases",
+ "count": "[length(coalesce(parameters('mongodbDatabases'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-mongodb-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()].name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()].name]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "collections": {
+ "value": "[tryGet(coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()], 'collections')]"
+ },
+ "throughput": {
+ "value": "[tryGet(coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()], 'throughput')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "16911349070369924403"
+ },
+ "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",
+ "nullable": true,
+ "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": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "mongodbDatabase": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases",
+ "apiVersion": "2024-11-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(coalesce(parameters('collections'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-collection-{1}', uniqueString(deployment().name, parameters('name')), coalesce(parameters('collections'), createArray())[copyIndex()].name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('databaseAccountName')]"
+ },
+ "mongodbDatabaseName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('collections'), createArray())[copyIndex()].name]"
+ },
+ "indexes": {
+ "value": "[coalesce(parameters('collections'), createArray())[copyIndex()].indexes]"
+ },
+ "shardKey": {
+ "value": "[coalesce(parameters('collections'), createArray())[copyIndex()].shardKey]"
+ },
+ "throughput": {
+ "value": "[tryGet(coalesce(parameters('collections'), createArray())[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.35.1.17967",
+ "templateHash": "7802955893269337475"
+ },
+ "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": [
+ "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]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "databaseAccount"
+ ]
+ },
+ "databaseAccount_gremlinDatabases": {
+ "copy": {
+ "name": "databaseAccount_gremlinDatabases",
+ "count": "[length(coalesce(parameters('gremlinDatabases'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-gremlin-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()].name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()].name]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "graphs": {
+ "value": "[tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'graphs')]"
+ },
+ "maxThroughput": {
+ "value": "[tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'maxThroughput')]"
+ },
+ "throughput": {
+ "value": "[tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'throughput')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "4743052544503629108"
+ },
+ "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": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "gremlinDatabase": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases",
+ "apiVersion": "2024-11-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.35.1.17967",
+ "templateHash": "9587717186996793648"
+ },
+ "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": [
+ "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]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "databaseAccount"
+ ]
+ },
+ "databaseAccount_tables": {
+ "copy": {
+ "name": "databaseAccount_tables",
+ "count": "[length(coalesce(parameters('tables'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-table-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('tables'), createArray())[copyIndex()].name)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "databaseAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('tables'), createArray())[copyIndex()].name]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "maxThroughput": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'maxThroughput')]"
+ },
+ "throughput": {
+ "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'throughput')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "14106261468136691896"
+ },
+ "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": "2024-11-15",
+ "name": "[parameters('databaseAccountName')]"
+ },
+ "table": {
+ "type": "Microsoft.DocumentDB/databaseAccounts/tables",
+ "apiVersion": "2024-11-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]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "databaseAccount"
+ ]
+ },
+ "databaseAccount_privateEndpoints": {
+ "copy": {
+ "name": "databaseAccount_privateEndpoints",
+ "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "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": {
+ "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": "[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.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": {
+ "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.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."
+ },
+ "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": [
+ "databaseAccount"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the database account."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the database account."
+ },
+ "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]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[tryGet(tryGet(reference('databaseAccount', '2024-11-15', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('databaseAccount', '2024-11-15', 'full').location]"
+ },
+ "endpoint": {
+ "type": "string",
+ "metadata": {
+ "description": "The endpoint of the database account."
+ },
+ "value": "[reference('databaseAccount').documentEndpoint]"
+ },
+ "privateEndpoints": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/privateEndpointOutputType"
+ },
+ "metadata": {
+ "description": "The private endpoints of the database account."
+ },
+ "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]"
+ }
+ }
+ },
+ "primaryReadWriteKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary read-write key."
+ },
+ "value": "[listKeys('databaseAccount', '2024-11-15').primaryMasterKey]"
+ },
+ "primaryReadOnlyKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary read-only key."
+ },
+ "value": "[listKeys('databaseAccount', '2024-11-15').primaryReadonlyMasterKey]"
+ },
+ "primaryReadWriteConnectionString": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary read-write connection string."
+ },
+ "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[0].connectionString]"
+ },
+ "primaryReadOnlyConnectionString": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary read-only connection string."
+ },
+ "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[2].connectionString]"
+ },
+ "secondaryReadWriteKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary read-write key."
+ },
+ "value": "[listKeys('databaseAccount', '2024-11-15').secondaryMasterKey]"
+ },
+ "secondaryReadOnlyKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary read-only key."
+ },
+ "value": "[listKeys('databaseAccount', '2024-11-15').secondaryReadonlyMasterKey]"
+ },
+ "secondaryReadWriteConnectionString": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary read-write connection string."
+ },
+ "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[1].connectionString]"
+ },
+ "secondaryReadOnlyConnectionString": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary read-only connection string."
+ },
+ "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[3].connectionString]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cosmosDb)]",
+ "logAnalyticsWorkspace",
+ "userAssignedIdentity",
+ "virtualNetwork"
+ ]
+ },
+ "containerAppEnvironment": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.app.managed-environment.{0}', variables('containerAppEnvironmentResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('containerAppEnvironmentResourceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "publicNetworkAccess": {
+ "value": "Enabled"
+ },
+ "internal": {
+ "value": false
+ },
+ "infrastructureSubnetResourceId": "[if(parameters('enablePrivateNetworking'), createObject('value', tryGet(tryGet(tryGet(if(parameters('enablePrivateNetworking'), reference('virtualNetwork'), null()), 'outputs'), 'containerSubnetResourceId'), 'value')), createObject('value', null()))]",
+ "appLogsConfiguration": "[if(parameters('enableMonitoring'), createObject('value', createObject('destination', 'log-analytics', 'logAnalyticsConfiguration', createObject('customerId', if(variables('useExistingLogAnalytics'), reference('existingLogAnalyticsWorkspace').customerId, reference('logAnalyticsWorkspace').outputs.logAnalyticsWorkspaceId.value), 'sharedKey', if(variables('useExistingLogAnalytics'), listKeys('existingLogAnalyticsWorkspace', '2020-08-01').primarySharedKey, listOutputsWithSecureValues('logAnalyticsWorkspace', '2025-04-01').primarySharedKey)))), createObject('value', null()))]",
+ "appInsightsConnectionString": "[if(parameters('enableMonitoring'), createObject('value', reference('applicationInsights').outputs.connectionString.value), createObject('value', null()))]",
+ "zoneRedundant": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]",
+ "infrastructureResourceGroupName": "[if(parameters('enableRedundancy'), createObject('value', format('{0}-infra', resourceGroup().name)), createObject('value', null()))]",
+ "workloadProfiles": "[if(parameters('enableRedundancy'), createObject('value', createArray(createObject('maximumCount', 3, 'minimumCount', 3, 'name', 'CAW01', 'workloadProfileType', 'D4'))), createObject('value', createArray(createObject('name', 'Consumption', 'workloadProfileType', 'Consumption'))))]"
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "10777649424390064640"
+ },
+ "name": "App ManagedEnvironments",
+ "description": "This module deploys an App Managed Environment (also known as a Container App Environment)."
+ },
+ "definitions": {
+ "certificateType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the certificate."
+ }
+ },
+ "certificateType": {
+ "type": "string",
+ "allowedValues": [
+ "ImagePullTrustedCA",
+ "ServerSSLCertificate"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The type of the certificate."
+ }
+ },
+ "certificateValue": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The value of the certificate. PFX or PEM blob."
+ }
+ },
+ "certificatePassword": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The password of the certificate."
+ }
+ },
+ "certificateKeyVaultProperties": {
+ "$ref": "#/definitions/certificateKeyVaultPropertiesType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A key vault reference."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a certificate."
+ }
+ },
+ "storageType": {
+ "type": "object",
+ "properties": {
+ "accessMode": {
+ "type": "string",
+ "allowedValues": [
+ "ReadOnly",
+ "ReadWrite"
+ ],
+ "metadata": {
+ "description": "Required. Access mode for storage: \"ReadOnly\" or \"ReadWrite\"."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "NFS",
+ "SMB"
+ ],
+ "metadata": {
+ "description": "Required. Type of storage: \"SMB\" or \"NFS\"."
+ }
+ },
+ "storageAccountName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Storage account name."
+ }
+ },
+ "shareName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. File share name."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type of the storage."
+ }
+ },
+ "appLogsConfigurationType": {
+ "type": "object",
+ "properties": {
+ "destination": {
+ "type": "string",
+ "allowedValues": [
+ "azure-monitor",
+ "log-analytics",
+ "none"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The destination of the logs."
+ }
+ },
+ "logAnalyticsConfiguration": {
+ "type": "object",
+ "properties": {
+ "customerId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Log Analytics Workspace ID."
+ }
+ },
+ "sharedKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "Required. The shared key of the Log Analytics workspace."
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The Log Analytics configuration. Required if `destination` is `log-analytics`."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the App Logs Configuration."
+ }
+ },
+ "certificateKeyVaultPropertiesType": {
+ "type": "object",
+ "properties": {
+ "identityResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the identity. This is the identity that will be used to access the key vault."
+ }
+ },
+ "keyVaultUrl": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A key vault URL referencing the wildcard certificate that will be used for the custom domain."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for the certificate's key vault properties.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "certificates/main.bicep"
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "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 Container Apps Managed Environment."
+ }
+ },
+ "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."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "appInsightsConnectionString": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Application Insights connection string."
+ }
+ },
+ "daprAIConnectionString": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Application Insights connection string used by Dapr to export Service to Service communication telemetry."
+ }
+ },
+ "daprAIInstrumentationKey": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry."
+ }
+ },
+ "dockerBridgeCidr": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Conditional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "infrastructureSubnetResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if \"internal\" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "internal": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Conditional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then \"infrastructureSubnetId\" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "platformReservedCidr": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Conditional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "platformReservedDnsIP": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Conditional. An IP address from the IP range defined by \"platformReservedCidr\" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "peerTrafficEncryption": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Whether or not to encrypt peer traffic."
+ }
+ },
+ "publicNetworkAccess": {
+ "type": "string",
+ "defaultValue": "Disabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. Whether to allow or block all public traffic."
+ }
+ },
+ "zoneRedundant": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Whether or not this Managed Environment is zone-redundant."
+ }
+ },
+ "certificatePassword": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Password of the certificate used by the custom domain."
+ }
+ },
+ "certificateValue": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Certificate to use for the custom domain. PFX or PEM."
+ }
+ },
+ "dnsSuffix": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. DNS suffix for the environment domain."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "openTelemetryConfiguration": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Open Telemetry configuration."
+ }
+ },
+ "workloadProfiles": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "infrastructureResourceGroupName": {
+ "type": "string",
+ "defaultValue": "[take(format('ME_{0}', parameters('name')), 63)]",
+ "metadata": {
+ "description": "Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant."
+ }
+ },
+ "storages": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/storageType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The list of storages to mount on the environment."
+ }
+ },
+ "certificate": {
+ "$ref": "#/definitions/certificateType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A Managed Environment Certificate."
+ }
+ },
+ "appLogsConfiguration": {
+ "$ref": "#/definitions/appLogsConfigurationType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The AppLogsConfiguration for the Managed Environment."
+ }
+ }
+ },
+ "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": {
+ "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": {
+ "managedEnvironment::storage": {
+ "copy": {
+ "name": "managedEnvironment::storage",
+ "count": "[length(coalesce(parameters('storages'), createArray()))]"
+ },
+ "type": "Microsoft.App/managedEnvironments/storages",
+ "apiVersion": "2024-10-02-preview",
+ "name": "[format('{0}/{1}', parameters('name'), coalesce(parameters('storages'), createArray())[copyIndex()].shareName)]",
+ "properties": {
+ "nfsAzureFile": "[if(equals(coalesce(parameters('storages'), createArray())[copyIndex()].kind, 'NFS'), createObject('accessMode', coalesce(parameters('storages'), createArray())[copyIndex()].accessMode, 'server', format('{0}.file.{1}', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName, environment().suffixes.storage), 'shareName', format('/{0}/{1}', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName, coalesce(parameters('storages'), createArray())[copyIndex()].shareName)), null())]",
+ "azureFile": "[if(equals(coalesce(parameters('storages'), createArray())[copyIndex()].kind, 'SMB'), createObject('accessMode', coalesce(parameters('storages'), createArray())[copyIndex()].accessMode, 'accountName', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName, 'accountKey', listkeys(resourceId('Microsoft.Storage/storageAccounts', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName), '2023-01-01').keys[0].value, 'shareName', coalesce(parameters('storages'), createArray())[copyIndex()].shareName), null())]"
+ },
+ "dependsOn": [
+ "managedEnvironment"
+ ]
+ },
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2024-11-01",
+ "name": "[format('46d3xbcp.res.app-managedenvironment.{0}.{1}', replace('0.11.2', '.', '-'), 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"
+ }
+ }
+ }
+ }
+ },
+ "managedEnvironment": {
+ "type": "Microsoft.App/managedEnvironments",
+ "apiVersion": "2024-10-02-preview",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
+ "properties": {
+ "appInsightsConfiguration": {
+ "connectionString": "[parameters('appInsightsConnectionString')]"
+ },
+ "appLogsConfiguration": "[parameters('appLogsConfiguration')]",
+ "daprAIConnectionString": "[parameters('daprAIConnectionString')]",
+ "daprAIInstrumentationKey": "[parameters('daprAIInstrumentationKey')]",
+ "customDomainConfiguration": {
+ "certificatePassword": "[parameters('certificatePassword')]",
+ "certificateValue": "[if(not(empty(parameters('certificateValue'))), parameters('certificateValue'), null())]",
+ "dnsSuffix": "[parameters('dnsSuffix')]",
+ "certificateKeyVaultProperties": "[if(not(empty(tryGet(parameters('certificate'), 'certificateKeyVaultProperties'))), createObject('identity', tryGet(parameters('certificate'), 'certificateKeyVaultProperties', 'identityResourceId'), 'keyVaultUrl', tryGet(parameters('certificate'), 'certificateKeyVaultProperties', 'keyVaultUrl')), null())]"
+ },
+ "openTelemetryConfiguration": "[if(not(empty(parameters('openTelemetryConfiguration'))), parameters('openTelemetryConfiguration'), null())]",
+ "peerTrafficConfiguration": {
+ "encryption": {
+ "enabled": "[parameters('peerTrafficEncryption')]"
+ }
+ },
+ "publicNetworkAccess": "[parameters('publicNetworkAccess')]",
+ "vnetConfiguration": {
+ "internal": "[parameters('internal')]",
+ "infrastructureSubnetId": "[if(not(empty(parameters('infrastructureSubnetResourceId'))), parameters('infrastructureSubnetResourceId'), null())]",
+ "dockerBridgeCidr": "[if(not(empty(parameters('infrastructureSubnetResourceId'))), parameters('dockerBridgeCidr'), null())]",
+ "platformReservedCidr": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetResourceId')))), parameters('platformReservedCidr'), null())]",
+ "platformReservedDnsIP": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetResourceId')))), parameters('platformReservedDnsIP'), null())]"
+ },
+ "workloadProfiles": "[if(not(empty(parameters('workloadProfiles'))), parameters('workloadProfiles'), null())]",
+ "zoneRedundant": "[parameters('zoneRedundant')]",
+ "infrastructureResourceGroup": "[parameters('infrastructureResourceGroupName')]"
+ }
+ },
+ "managedEnvironment_roleAssignments": {
+ "copy": {
+ "name": "managedEnvironment_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.App/managedEnvironments/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/managedEnvironments', 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": [
+ "managedEnvironment"
+ ]
+ },
+ "managedEnvironment_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.App/managedEnvironments/{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": [
+ "managedEnvironment"
+ ]
+ },
+ "managedEnvironment_certificate": {
+ "condition": "[not(empty(parameters('certificate')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-Managed-Environment-Certificate', uniqueString(deployment().name))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[coalesce(tryGet(parameters('certificate'), 'name'), format('cert-{0}', parameters('name')))]"
+ },
+ "managedEnvironmentName": {
+ "value": "[parameters('name')]"
+ },
+ "certificateKeyVaultProperties": {
+ "value": "[tryGet(parameters('certificate'), 'certificateKeyVaultProperties')]"
+ },
+ "certificateType": {
+ "value": "[tryGet(parameters('certificate'), 'certificateType')]"
+ },
+ "certificateValue": {
+ "value": "[tryGet(parameters('certificate'), 'certificateValue')]"
+ },
+ "certificatePassword": {
+ "value": "[tryGet(parameters('certificate'), 'certificatePassword')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "18123249047188753287"
+ },
+ "name": "App ManagedEnvironments Certificates",
+ "description": "This module deploys a App Managed Environment Certificate."
+ },
+ "definitions": {
+ "certificateKeyVaultPropertiesType": {
+ "type": "object",
+ "properties": {
+ "identityResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the identity. This is the identity that will be used to access the key vault."
+ }
+ },
+ "keyVaultUrl": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A key vault URL referencing the wildcard certificate that will be used for the custom domain."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the certificate's key vault properties."
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Container Apps Managed Environment Certificate."
+ }
+ },
+ "managedEnvironmentName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent app managed environment. Required if the template is used in a standalone deployment."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "certificateKeyVaultProperties": {
+ "$ref": "#/definitions/certificateKeyVaultPropertiesType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A key vault reference to the certificate to use for the custom domain."
+ }
+ },
+ "certificateType": {
+ "type": "string",
+ "nullable": true,
+ "allowedValues": [
+ "ServerSSLCertificate",
+ "ImagePullTrustedCA"
+ ],
+ "metadata": {
+ "description": "Optional. The type of the certificate."
+ }
+ },
+ "certificateValue": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The value of the certificate. PFX or PEM blob."
+ }
+ },
+ "certificatePassword": {
+ "type": "securestring",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The password of the certificate."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ }
+ },
+ "resources": {
+ "managedEnvironment": {
+ "existing": true,
+ "type": "Microsoft.App/managedEnvironments",
+ "apiVersion": "2024-10-02-preview",
+ "name": "[parameters('managedEnvironmentName')]"
+ },
+ "managedEnvironmentCertificate": {
+ "type": "Microsoft.App/managedEnvironments/certificates",
+ "apiVersion": "2024-10-02-preview",
+ "name": "[format('{0}/{1}', parameters('managedEnvironmentName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "properties": {
+ "certificateKeyVaultProperties": "[if(not(empty(parameters('certificateKeyVaultProperties'))), createObject('identity', parameters('certificateKeyVaultProperties').identityResourceId, 'keyVaultUrl', parameters('certificateKeyVaultProperties').keyVaultUrl), null())]",
+ "certificateType": "[parameters('certificateType')]",
+ "password": "[parameters('certificatePassword')]",
+ "value": "[parameters('certificateValue')]"
+ },
+ "tags": "[parameters('tags')]"
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the key values."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the key values."
+ },
+ "value": "[resourceId('Microsoft.App/managedEnvironments/certificates', parameters('managedEnvironmentName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the batch account was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "managedEnvironment"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the Managed Environment was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('managedEnvironment', '2024-10-02-preview', 'full').location]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Managed Environment."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Managed Environment."
+ },
+ "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[tryGet(tryGet(reference('managedEnvironment', '2024-10-02-preview', 'full'), 'identity'), 'principalId')]"
+ },
+ "defaultDomain": {
+ "type": "string",
+ "metadata": {
+ "description": "The Default domain of the Managed Environment."
+ },
+ "value": "[reference('managedEnvironment').defaultDomain]"
+ },
+ "staticIp": {
+ "type": "string",
+ "metadata": {
+ "description": "The IP address of the Managed Environment."
+ },
+ "value": "[reference('managedEnvironment').staticIp]"
+ },
+ "domainVerificationId": {
+ "type": "string",
+ "metadata": {
+ "description": "The domain verification id for custom domains."
+ },
+ "value": "[reference('managedEnvironment').customDomainConfiguration.customDomainVerificationId]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "applicationInsights",
+ "existingLogAnalyticsWorkspace",
+ "logAnalyticsWorkspace",
+ "virtualNetwork"
+ ]
+ },
+ "containerApp": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.app.container-app.{0}', variables('containerAppResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('containerAppResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "environmentResourceId": {
+ "value": "[reference('containerAppEnvironment').outputs.resourceId.value]"
+ },
+ "managedIdentities": {
+ "value": {
+ "userAssignedResourceIds": [
+ "[reference('userAssignedIdentity').outputs.resourceId.value]"
+ ]
+ }
+ },
+ "ingressTargetPort": {
+ "value": 8000
+ },
+ "ingressExternal": {
+ "value": true
+ },
+ "activeRevisionsMode": {
+ "value": "Single"
+ },
+ "corsPolicy": {
+ "value": {
+ "allowedOrigins": [
+ "[format('https://{0}.azurewebsites.net', variables('webSiteResourceName'))]",
+ "[format('http://{0}.azurewebsites.net', variables('webSiteResourceName'))]"
+ ],
+ "allowedMethods": [
+ "GET",
+ "POST",
+ "PUT",
+ "DELETE",
+ "OPTIONS"
+ ]
+ }
+ },
+ "scaleSettings": {
+ "value": {
+ "maxReplicas": "[if(parameters('enableScalability'), 3, 1)]",
+ "minReplicas": "[if(parameters('enableScalability'), 1, 1)]",
+ "rules": [
+ {
+ "name": "http-scaler",
+ "http": {
+ "metadata": {
+ "concurrentRequests": "100"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "containers": {
+ "value": [
+ {
+ "name": "backend",
+ "image": "[format('{0}/{1}:{2}', parameters('backendContainerRegistryHostname'), parameters('backendContainerImageName'), parameters('backendContainerImageTag'))]",
+ "resources": {
+ "cpu": "2.0",
+ "memory": "4.0Gi"
+ },
+ "env": [
+ {
+ "name": "COSMOSDB_ENDPOINT",
+ "value": "[format('https://{0}.documents.azure.com:443/', variables('cosmosDbResourceName'))]"
+ },
+ {
+ "name": "COSMOSDB_DATABASE",
+ "value": "[variables('cosmosDbDatabaseName')]"
+ },
+ {
+ "name": "COSMOSDB_CONTAINER",
+ "value": "[variables('cosmosDbDatabaseMemoryContainerName')]"
+ },
+ {
+ "name": "AZURE_OPENAI_ENDPOINT",
+ "value": "[format('https://{0}.openai.azure.com/', variables('aiFoundryAiServicesResourceName'))]"
+ },
+ {
+ "name": "AZURE_OPENAI_MODEL_NAME",
+ "value": "[variables('aiFoundryAiServicesModelDeployment').name]"
+ },
+ {
+ "name": "AZURE_OPENAI_DEPLOYMENT_NAME",
+ "value": "[variables('aiFoundryAiServicesModelDeployment').name]"
+ },
+ {
+ "name": "AZURE_OPENAI_RAI_DEPLOYMENT_NAME",
+ "value": "[variables('aiFoundryAiServices4_1ModelDeployment').name]"
+ },
+ {
+ "name": "AZURE_OPENAI_API_VERSION",
+ "value": "[parameters('azureopenaiVersion')]"
+ },
+ {
+ "name": "APPLICATIONINSIGHTS_INSTRUMENTATION_KEY",
+ "value": "[if(parameters('enableMonitoring'), reference('applicationInsights').outputs.instrumentationKey.value, '')]"
+ },
+ {
+ "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
+ "value": "[if(parameters('enableMonitoring'), reference('applicationInsights').outputs.connectionString.value, '')]"
+ },
+ {
+ "name": "AZURE_AI_SUBSCRIPTION_ID",
+ "value": "[variables('aiFoundryAiServicesSubscriptionId')]"
+ },
+ {
+ "name": "AZURE_AI_RESOURCE_GROUP",
+ "value": "[variables('aiFoundryAiServicesResourceGroupName')]"
+ },
+ {
+ "name": "AZURE_AI_PROJECT_NAME",
+ "value": "[if(variables('useExistingAiFoundryAiProject'), variables('aiFoundryAiProjectResourceName'), reference('aiFoundryAiServicesProject').outputs.name.value)]"
+ },
+ {
+ "name": "FRONTEND_SITE_NAME",
+ "value": "[format('https://{0}.azurewebsites.net', variables('webSiteResourceName'))]"
+ },
+ {
+ "name": "AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME",
+ "value": "[variables('aiFoundryAiServicesModelDeployment').name]"
+ },
+ {
+ "name": "APP_ENV",
+ "value": "Prod"
+ },
+ {
+ "name": "AZURE_AI_SEARCH_CONNECTION_NAME",
+ "value": "[variables('aiSearchConnectionName')]"
+ },
+ {
+ "name": "AZURE_AI_SEARCH_ENDPOINT",
+ "value": "[reference('searchServiceUpdate').outputs.endpoint.value]"
+ },
+ {
+ "name": "AZURE_COGNITIVE_SERVICES",
+ "value": "https://cognitiveservices.azure.com/.default"
+ },
+ {
+ "name": "AZURE_BING_CONNECTION_NAME",
+ "value": "binggrnd"
+ },
+ {
+ "name": "BING_CONNECTION_NAME",
+ "value": "binggrnd"
+ },
+ {
+ "name": "REASONING_MODEL_NAME",
+ "value": "[variables('aiFoundryAiServicesReasoningModelDeployment').name]"
+ },
+ {
+ "name": "MCP_SERVER_ENDPOINT",
+ "value": "[format('https://{0}/mcp', reference('containerAppMcp').outputs.fqdn.value)]"
+ },
+ {
+ "name": "MCP_SERVER_NAME",
+ "value": "MacaeMcpServer"
+ },
+ {
+ "name": "MCP_SERVER_DESCRIPTION",
+ "value": "MCP server with greeting, HR, and planning tools"
+ },
+ {
+ "name": "AZURE_TENANT_ID",
+ "value": "[tenant().tenantId]"
+ },
+ {
+ "name": "AZURE_CLIENT_ID",
+ "value": "[reference('userAssignedIdentity').outputs.clientId.value]"
+ },
+ {
+ "name": "SUPPORTED_MODELS",
+ "value": "[[\"o3\",\"o4-mini\",\"gpt-4.1\",\"gpt-4.1-mini\"]"
+ },
+ {
+ "name": "AZURE_STORAGE_BLOB_URL",
+ "value": "[reference('avmStorageAccount').outputs.serviceEndpoints.value.blob]"
+ },
+ {
+ "name": "AZURE_AI_PROJECT_ENDPOINT",
+ "value": "[if(variables('useExistingAiFoundryAiProject'), reference('existingAiFoundryAiServicesProject').endpoints['AI Foundry API'], reference('aiFoundryAiServicesProject').outputs.apiEndpoint.value)]"
+ },
+ {
+ "name": "AZURE_AI_AGENT_ENDPOINT",
+ "value": "[if(variables('useExistingAiFoundryAiProject'), reference('existingAiFoundryAiServicesProject').endpoints['AI Foundry API'], reference('aiFoundryAiServicesProject').outputs.apiEndpoint.value)]"
+ },
+ {
+ "name": "AZURE_AI_AGENT_API_VERSION",
+ "value": "[parameters('azureAiAgentAPIVersion')]"
+ },
+ {
+ "name": "AZURE_AI_AGENT_PROJECT_CONNECTION_STRING",
+ "value": "[format('{0}.services.ai.azure.com;{1};{2};{3}', variables('aiFoundryAiServicesResourceName'), variables('aiFoundryAiServicesSubscriptionId'), variables('aiFoundryAiServicesResourceGroupName'), variables('aiFoundryAiProjectResourceName'))]"
+ },
+ {
+ "name": "AZURE_BASIC_LOGGING_LEVEL",
+ "value": "INFO"
+ },
+ {
+ "name": "AZURE_PACKAGE_LOGGING_LEVEL",
+ "value": "WARNING"
+ },
+ {
+ "name": "AZURE_LOGGING_PACKAGES",
+ "value": ""
+ }
+ ]
+ }
+ ]
+ },
+ "secrets": {
+ "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.177.2456",
+ "templateHash": "13502451048865419001"
+ },
+ "name": "Container Apps",
+ "description": "This module deploys a Container App."
+ },
+ "definitions": {
+ "containerType": {
+ "type": "object",
+ "properties": {
+ "args": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container start command arguments."
+ }
+ },
+ "command": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container start command."
+ }
+ },
+ "env": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/environmentVarType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container environment variables."
+ }
+ },
+ "image": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Container image tag."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Custom container name."
+ }
+ },
+ "probes": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/containerAppProbeType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of probes for the container."
+ }
+ },
+ "resources": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Container resource requirements."
+ }
+ },
+ "volumeMounts": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/volumeMountType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container volume mounts."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a container."
+ }
+ },
+ "ingressPortMappingType": {
+ "type": "object",
+ "properties": {
+ "exposedPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the exposed port for the target port. If not specified, it defaults to target port."
+ }
+ },
+ "external": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Specifies whether the app port is accessible outside of the environment."
+ }
+ },
+ "targetPort": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. Specifies the port the container listens on."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an ingress port mapping."
+ }
+ },
+ "serviceBindingType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the service."
+ }
+ },
+ "serviceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The service ID."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a service binding."
+ }
+ },
+ "environmentVarType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Environment variable name."
+ }
+ },
+ "secretRef": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the Container App secret from which to pull the environment variable value."
+ }
+ },
+ "value": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Non-secret environment variable value."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an environment variable."
+ }
+ },
+ "containerAppProbeType": {
+ "type": "object",
+ "properties": {
+ "failureThreshold": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 10,
+ "metadata": {
+ "description": "Optional. Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3."
+ }
+ },
+ "httpGet": {
+ "$ref": "#/definitions/containerAppProbeHttpGetType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. HTTPGet specifies the http request to perform."
+ }
+ },
+ "initialDelaySeconds": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 60,
+ "metadata": {
+ "description": "Optional. Number of seconds after the container has started before liveness probes are initiated."
+ }
+ },
+ "periodSeconds": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 240,
+ "metadata": {
+ "description": "Optional. How often (in seconds) to perform the probe. Default to 10 seconds."
+ }
+ },
+ "successThreshold": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 10,
+ "metadata": {
+ "description": "Optional. Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup."
+ }
+ },
+ "tcpSocket": {
+ "$ref": "#/definitions/containerAppProbeTcpSocketType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The TCP socket specifies an action involving a TCP port. TCP hooks not yet supported."
+ }
+ },
+ "terminationGracePeriodSeconds": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate. Maximum value is 3600 seconds (1 hour)."
+ }
+ },
+ "timeoutSeconds": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 240,
+ "metadata": {
+ "description": "Optional. Number of seconds after which the probe times out. Defaults to 1 second."
+ }
+ },
+ "type": {
+ "type": "string",
+ "allowedValues": [
+ "Liveness",
+ "Readiness",
+ "Startup"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The type of probe."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe."
+ }
+ },
+ "corsPolicyType": {
+ "type": "object",
+ "properties": {
+ "allowCredentials": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Switch to determine whether the resource allows credentials."
+ }
+ },
+ "allowedHeaders": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-allow-headers header."
+ }
+ },
+ "allowedMethods": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-allow-methods header."
+ }
+ },
+ "allowedOrigins": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-allow-origins header."
+ }
+ },
+ "exposeHeaders": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-expose-headers header."
+ }
+ },
+ "maxAge": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-max-age header."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a CORS policy."
+ }
+ },
+ "containerAppProbeHttpGetType": {
+ "type": "object",
+ "properties": {
+ "host": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Host name to connect to. Defaults to the pod IP."
+ }
+ },
+ "httpHeaders": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/containerAppProbeHttpGetHeadersItemType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. HTTP headers to set in the request."
+ }
+ },
+ "path": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Path to access on the HTTP server."
+ }
+ },
+ "port": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. Name or number of the port to access on the container."
+ }
+ },
+ "scheme": {
+ "type": "string",
+ "allowedValues": [
+ "HTTP",
+ "HTTPS"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Scheme to use for connecting to the host. Defaults to HTTP."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe HTTP GET."
+ }
+ },
+ "containerAppProbeHttpGetHeadersItemType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the header."
+ }
+ },
+ "value": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Value of the header."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe HTTP GET header."
+ }
+ },
+ "containerAppProbeTcpSocketType": {
+ "type": "object",
+ "properties": {
+ "host": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Host name to connect to, defaults to the pod IP."
+ }
+ },
+ "port": {
+ "type": "int",
+ "minValue": 1,
+ "maxValue": 65535,
+ "metadata": {
+ "description": "Required. Number of the port to access on the container. Name must be an IANA_SVC_NAME."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe TCP socket."
+ }
+ },
+ "scaleType": {
+ "type": "object",
+ "properties": {
+ "maxReplicas": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. The maximum number of replicas."
+ }
+ },
+ "minReplicas": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. The minimum number of replicas."
+ }
+ },
+ "cooldownPeriod": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The cooldown period in seconds."
+ }
+ },
+ "pollingInterval": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The polling interval in seconds."
+ }
+ },
+ "rules": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/scaleRuleType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The scaling rules."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The scale settings for the Container App."
+ }
+ },
+ "scaleRuleType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the scaling rule."
+ }
+ },
+ "custom": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The custom scaling rule."
+ }
+ },
+ "azureQueue": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Azure Queue based scaling rule."
+ }
+ },
+ "http": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The HTTP requests based scaling rule."
+ }
+ },
+ "tcp": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The TCP based scaling rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The scaling rules for the Container App."
+ }
+ },
+ "volumeMountType": {
+ "type": "object",
+ "properties": {
+ "mountPath": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Path within the container at which the volume should be mounted.Must not contain ':'."
+ }
+ },
+ "subPath": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root)."
+ }
+ },
+ "volumeName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. This must match the Name of a Volume."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a volume mount."
+ }
+ },
+ "secretType": {
+ "type": "object",
+ "properties": {
+ "identity": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity."
+ }
+ },
+ "keyVaultUrl": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The URL of the Azure Key Vault secret referenced by the Container App. Required if `value` is null."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the container app secret."
+ }
+ },
+ "value": {
+ "type": "securestring",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The container app secret value, if not fetched from the Key Vault. Required if `keyVaultUrl` is not null."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a secret."
+ }
+ },
+ "authConfigType": {
+ "type": "object",
+ "properties": {
+ "encryptionSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/encryptionSettings"
+ },
+ "description": "Optional. The configuration settings of the secrets references of encryption key and signing key for ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "globalValidation": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/globalValidation"
+ },
+ "description": "Optional. The configuration settings that determines the validation flow of users using Service Authentication and/or Authorization."
+ },
+ "nullable": true
+ },
+ "httpSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/httpSettings"
+ },
+ "description": "Optional. The configuration settings of the HTTP requests for authentication and authorization requests made against ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "identityProviders": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/identityProviders"
+ },
+ "description": "Optional. The configuration settings of each of the identity providers used to configure ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "login": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/login"
+ },
+ "description": "Optional. The configuration settings of the login flow of users using ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "platform": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/platform"
+ },
+ "description": "Optional. The configuration settings of the platform of ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the container app's authentication configuration."
+ }
+ },
+ "diagnosticSettingMetricsOnlyType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
+ }
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a diagnostic setting. To be used if only metrics are supported by the resource provider.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "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"
+ },
+ "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.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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Container App."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "disableIngress": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Bool to disable all ingress traffic for the container app."
+ }
+ },
+ "ingressExternal": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Bool indicating if the App exposes an external HTTP endpoint."
+ }
+ },
+ "clientCertificateMode": {
+ "type": "string",
+ "defaultValue": "ignore",
+ "allowedValues": [
+ "accept",
+ "ignore",
+ "require"
+ ],
+ "metadata": {
+ "description": "Optional. Client certificate mode for mTLS."
+ }
+ },
+ "corsPolicy": {
+ "$ref": "#/definitions/corsPolicyType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Object userd to configure CORS policy."
+ }
+ },
+ "stickySessionsAffinity": {
+ "type": "string",
+ "defaultValue": "none",
+ "allowedValues": [
+ "none",
+ "sticky"
+ ],
+ "metadata": {
+ "description": "Optional. Bool indicating if the Container App should enable session affinity."
+ }
+ },
+ "ingressTransport": {
+ "type": "string",
+ "defaultValue": "auto",
+ "allowedValues": [
+ "auto",
+ "http",
+ "http2",
+ "tcp"
+ ],
+ "metadata": {
+ "description": "Optional. Ingress transport protocol."
+ }
+ },
+ "service": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/service"
+ },
+ "description": "Optional. Dev ContainerApp service type."
+ },
+ "nullable": true
+ },
+ "includeAddOns": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Toggle to include the service configuration."
+ }
+ },
+ "additionalPortMappings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ingressPortMappingType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Settings to expose additional ports on container app."
+ }
+ },
+ "ingressAllowInsecure": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Bool indicating if HTTP connections to is allowed. If set to false HTTP connections are automatically redirected to HTTPS connections."
+ }
+ },
+ "ingressTargetPort": {
+ "type": "int",
+ "defaultValue": 80,
+ "metadata": {
+ "description": "Optional. Target Port in containers for traffic from ingress."
+ }
+ },
+ "scaleSettings": {
+ "$ref": "#/definitions/scaleType",
+ "defaultValue": {
+ "maxReplicas": 10,
+ "minReplicas": 3
+ },
+ "metadata": {
+ "description": "Optional. The scaling settings of the service."
+ }
+ },
+ "serviceBinds": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/serviceBindingType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of container app services bound to the app."
+ }
+ },
+ "activeRevisionsMode": {
+ "type": "string",
+ "defaultValue": "Single",
+ "allowedValues": [
+ "Multiple",
+ "Single"
+ ],
+ "metadata": {
+ "description": "Optional. Controls how active revisions are handled for the Container app."
+ }
+ },
+ "environmentResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of environment."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "registries": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/registries"
+ },
+ "description": "Optional. Collection of private container registry credentials for containers used by the Container app."
+ },
+ "nullable": true
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "customDomains": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/ingress/properties/customDomains"
+ },
+ "description": "Optional. Custom domain bindings for Container App hostnames."
+ },
+ "nullable": true
+ },
+ "exposedPort": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Exposed Port in containers for TCP traffic from ingress."
+ }
+ },
+ "ipSecurityRestrictions": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/ingress/properties/ipSecurityRestrictions"
+ },
+ "description": "Optional. Rules to restrict incoming IP address."
+ },
+ "nullable": true
+ },
+ "trafficLabel": {
+ "type": "string",
+ "defaultValue": "label-1",
+ "metadata": {
+ "description": "Optional. Associates a traffic label with a revision. Label name should be consist of lower case alphanumeric characters or dashes."
+ }
+ },
+ "trafficLatestRevision": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Indicates that the traffic weight belongs to a latest stable revision."
+ }
+ },
+ "trafficRevisionName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Name of a revision."
+ }
+ },
+ "trafficWeight": {
+ "type": "int",
+ "defaultValue": 100,
+ "metadata": {
+ "description": "Optional. Traffic weight assigned to a revision."
+ }
+ },
+ "dapr": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/dapr"
+ },
+ "description": "Optional. Dapr configuration for the Container App."
+ },
+ "nullable": true
+ },
+ "identitySettings": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/identitySettings"
+ },
+ "description": "Optional. Settings for Managed Identities that are assigned to the Container App. If a Managed Identity is not specified here, default settings will be used."
+ },
+ "nullable": true
+ },
+ "maxInactiveRevisions": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Max inactive revisions a Container App can have."
+ }
+ },
+ "runtime": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/runtime"
+ },
+ "description": "Optional. Runtime configuration for the Container App."
+ },
+ "nullable": true
+ },
+ "containers": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/containerType"
+ },
+ "metadata": {
+ "description": "Required. List of container definitions for the Container App."
+ }
+ },
+ "initContainersTemplate": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/template/properties/initContainers"
+ },
+ "description": "Optional. List of specialized containers that run before app containers."
+ },
+ "nullable": true
+ },
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/secretType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The secrets of the Container App."
+ }
+ },
+ "revisionSuffix": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. User friendly suffix that is appended to the revision name."
+ }
+ },
+ "volumes": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/template/properties/volumes"
+ },
+ "description": "Optional. List of volume definitions for the Container App."
+ },
+ "nullable": true
+ },
+ "workloadProfileName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Workload profile name to pin for container app execution."
+ }
+ },
+ "authConfig": {
+ "$ref": "#/definitions/authConfigType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the Container App Auth configs."
+ }
+ },
+ "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)))))]"
+ }
+ ],
+ "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": {
+ "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]",
+ "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.app-containerapp.{0}.{1}', replace('0.18.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"
+ }
+ }
+ }
+ }
+ },
+ "containerApp": {
+ "type": "Microsoft.App/containerApps",
+ "apiVersion": "2025-01-01",
+ "name": "[parameters('name')]",
+ "tags": "[parameters('tags')]",
+ "location": "[parameters('location')]",
+ "identity": "[variables('identity')]",
+ "properties": {
+ "environmentId": "[parameters('environmentResourceId')]",
+ "workloadProfileName": "[parameters('workloadProfileName')]",
+ "template": {
+ "containers": "[parameters('containers')]",
+ "initContainers": "[if(not(empty(parameters('initContainersTemplate'))), parameters('initContainersTemplate'), null())]",
+ "revisionSuffix": "[parameters('revisionSuffix')]",
+ "scale": "[parameters('scaleSettings')]",
+ "serviceBinds": "[if(and(parameters('includeAddOns'), not(empty(parameters('serviceBinds')))), parameters('serviceBinds'), null())]",
+ "volumes": "[if(not(empty(parameters('volumes'))), parameters('volumes'), null())]"
+ },
+ "configuration": {
+ "activeRevisionsMode": "[parameters('activeRevisionsMode')]",
+ "dapr": "[if(not(empty(parameters('dapr'))), parameters('dapr'), null())]",
+ "identitySettings": "[if(not(empty(parameters('identitySettings'))), parameters('identitySettings'), null())]",
+ "ingress": "[if(parameters('disableIngress'), null(), createObject('additionalPortMappings', parameters('additionalPortMappings'), 'allowInsecure', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('ingressAllowInsecure'), false()), 'customDomains', if(not(empty(parameters('customDomains'))), parameters('customDomains'), null()), 'corsPolicy', if(and(not(equals(parameters('corsPolicy'), null())), not(equals(parameters('ingressTransport'), 'tcp'))), createObject('allowCredentials', coalesce(tryGet(parameters('corsPolicy'), 'allowCredentials'), false()), 'allowedHeaders', coalesce(tryGet(parameters('corsPolicy'), 'allowedHeaders'), createArray()), 'allowedMethods', coalesce(tryGet(parameters('corsPolicy'), 'allowedMethods'), createArray()), 'allowedOrigins', coalesce(tryGet(parameters('corsPolicy'), 'allowedOrigins'), createArray()), 'exposeHeaders', coalesce(tryGet(parameters('corsPolicy'), 'exposeHeaders'), createArray()), 'maxAge', tryGet(parameters('corsPolicy'), 'maxAge')), null()), 'clientCertificateMode', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('clientCertificateMode'), null()), 'exposedPort', parameters('exposedPort'), 'external', parameters('ingressExternal'), 'ipSecurityRestrictions', if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null()), 'targetPort', parameters('ingressTargetPort'), 'stickySessions', createObject('affinity', parameters('stickySessionsAffinity')), 'traffic', if(not(equals(parameters('ingressTransport'), 'tcp')), createArray(createObject('label', parameters('trafficLabel'), 'latestRevision', parameters('trafficLatestRevision'), 'revisionName', parameters('trafficRevisionName'), 'weight', parameters('trafficWeight'))), null()), 'transport', parameters('ingressTransport')))]",
+ "service": "[if(and(parameters('includeAddOns'), not(empty(parameters('service')))), parameters('service'), null())]",
+ "maxInactiveRevisions": "[parameters('maxInactiveRevisions')]",
+ "registries": "[if(not(empty(parameters('registries'))), parameters('registries'), null())]",
+ "secrets": "[parameters('secrets')]",
+ "runtime": "[if(not(empty(parameters('runtime'))), parameters('runtime'), null())]"
+ }
+ }
+ },
+ "containerApp_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.App/containerApps/{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": [
+ "containerApp"
+ ]
+ },
+ "containerApp_roleAssignments": {
+ "copy": {
+ "name": "containerApp_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.App/containerApps/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/containerApps', 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": [
+ "containerApp"
+ ]
+ },
+ "containerApp_diagnosticSettings": {
+ "copy": {
+ "name": "containerApp_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.App/containerApps/{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": [
+ "containerApp"
+ ]
+ },
+ "containerAppAuthConfigs": {
+ "condition": "[not(empty(parameters('authConfig')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-auth-config', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "containerAppName": {
+ "value": "[parameters('name')]"
+ },
+ "encryptionSettings": {
+ "value": "[tryGet(parameters('authConfig'), 'encryptionSettings')]"
+ },
+ "globalValidation": {
+ "value": "[tryGet(parameters('authConfig'), 'globalValidation')]"
+ },
+ "httpSettings": {
+ "value": "[tryGet(parameters('authConfig'), 'httpSettings')]"
+ },
+ "identityProviders": {
+ "value": "[tryGet(parameters('authConfig'), 'identityProviders')]"
+ },
+ "login": {
+ "value": "[tryGet(parameters('authConfig'), 'login')]"
+ },
+ "platform": {
+ "value": "[tryGet(parameters('authConfig'), 'platform')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "9975390462196064744"
+ },
+ "name": "Container App Auth Configs",
+ "description": "This module deploys Container App Auth Configs."
+ },
+ "parameters": {
+ "containerAppName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Container App. Required if the template is used in a standalone deployment."
+ }
+ },
+ "encryptionSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/encryptionSettings"
+ },
+ "description": "Optional. The configuration settings of the secrets references of encryption key and signing key for ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "globalValidation": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/globalValidation"
+ },
+ "description": "Optional. The configuration settings that determines the validation flow of users using Service Authentication and/or Authorization."
+ },
+ "nullable": true
+ },
+ "httpSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/httpSettings"
+ },
+ "description": "Optional. The configuration settings of the HTTP requests for authentication and authorization requests made against ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "identityProviders": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/identityProviders"
+ },
+ "description": "Optional. The configuration settings of each of the identity providers used to configure ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "login": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/login"
+ },
+ "description": "Optional. The configuration settings of the login flow of users using ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "platform": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/platform"
+ },
+ "description": "Optional. The configuration settings of the platform of ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ }
+ },
+ "resources": {
+ "containerApp": {
+ "existing": true,
+ "type": "Microsoft.App/containerApps",
+ "apiVersion": "2025-01-01",
+ "name": "[parameters('containerAppName')]"
+ },
+ "containerAppAuthConfigs": {
+ "type": "Microsoft.App/containerApps/authConfigs",
+ "apiVersion": "2025-01-01",
+ "name": "[format('{0}/{1}', parameters('containerAppName'), 'current')]",
+ "properties": {
+ "encryptionSettings": "[parameters('encryptionSettings')]",
+ "globalValidation": "[parameters('globalValidation')]",
+ "httpSettings": "[parameters('httpSettings')]",
+ "identityProviders": "[parameters('identityProviders')]",
+ "login": "[parameters('login')]",
+ "platform": "[parameters('platform')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the set of Container App Auth configs."
+ },
+ "value": "current"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the set of Container App Auth configs."
+ },
+ "value": "[resourceId('Microsoft.App/containerApps/authConfigs', parameters('containerAppName'), 'current')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group containing the set of Container App Auth configs."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "containerApp"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Container App."
+ },
+ "value": "[resourceId('Microsoft.App/containerApps', parameters('name'))]"
+ },
+ "fqdn": {
+ "type": "string",
+ "metadata": {
+ "description": "The configuration of ingress fqdn."
+ },
+ "value": "[if(parameters('disableIngress'), 'IngressDisabled', reference('containerApp').configuration.ingress.fqdn)]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the Container App was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Container App."
+ },
+ "value": "[parameters('name')]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[tryGet(tryGet(reference('containerApp', '2025-01-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('containerApp', '2025-01-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "aiFoundryAiServicesProject",
+ "applicationInsights",
+ "avmStorageAccount",
+ "containerAppEnvironment",
+ "containerAppMcp",
+ "existingAiFoundryAiServicesProject",
+ "searchServiceUpdate",
+ "userAssignedIdentity"
+ ]
+ },
+ "containerAppMcp": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.app.container-app.{0}', variables('containerAppMcpResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('containerAppMcpResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "environmentResourceId": {
+ "value": "[reference('containerAppEnvironment').outputs.resourceId.value]"
+ },
+ "managedIdentities": {
+ "value": {
+ "userAssignedResourceIds": [
+ "[reference('userAssignedIdentity').outputs.resourceId.value]"
+ ]
+ }
+ },
+ "ingressTargetPort": {
+ "value": 9000
+ },
+ "ingressExternal": {
+ "value": true
+ },
+ "activeRevisionsMode": {
+ "value": "Single"
+ },
+ "corsPolicy": {
+ "value": {
+ "allowedOrigins": [
+ "[format('https://{0}.azurewebsites.net', variables('webSiteResourceName'))]",
+ "[format('http://{0}.azurewebsites.net', variables('webSiteResourceName'))]"
+ ]
+ }
+ },
+ "scaleSettings": {
+ "value": {
+ "maxReplicas": "[if(parameters('enableScalability'), 3, 1)]",
+ "minReplicas": "[if(parameters('enableScalability'), 1, 1)]",
+ "rules": [
+ {
+ "name": "http-scaler",
+ "http": {
+ "metadata": {
+ "concurrentRequests": "100"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "containers": {
+ "value": [
+ {
+ "name": "mcp",
+ "image": "[format('{0}/{1}:{2}', parameters('MCPContainerRegistryHostname'), parameters('MCPContainerImageName'), parameters('MCPContainerImageTag'))]",
+ "resources": {
+ "cpu": "2.0",
+ "memory": "4.0Gi"
+ },
+ "env": [
+ {
+ "name": "HOST",
+ "value": "0.0.0.0"
+ },
+ {
+ "name": "PORT",
+ "value": "9000"
+ },
+ {
+ "name": "DEBUG",
+ "value": "false"
+ },
+ {
+ "name": "SERVER_NAME",
+ "value": "MacaeMcpServer"
+ },
+ {
+ "name": "ENABLE_AUTH",
+ "value": "false"
+ },
+ {
+ "name": "TENANT_ID",
+ "value": "[tenant().tenantId]"
+ },
+ {
+ "name": "CLIENT_ID",
+ "value": "[reference('userAssignedIdentity').outputs.clientId.value]"
+ },
+ {
+ "name": "JWKS_URI",
+ "value": "[format('https://login.microsoftonline.com/{0}/discovery/v2.0/keys', tenant().tenantId)]"
+ },
+ {
+ "name": "ISSUER",
+ "value": "[format('https://sts.windows.net/{0}/', tenant().tenantId)]"
+ },
+ {
+ "name": "AUDIENCE",
+ "value": "[format('api://{0}', reference('userAssignedIdentity').outputs.clientId.value)]"
+ },
+ {
+ "name": "DATASET_PATH",
+ "value": "./datasets"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "13502451048865419001"
+ },
+ "name": "Container Apps",
+ "description": "This module deploys a Container App."
+ },
+ "definitions": {
+ "containerType": {
+ "type": "object",
+ "properties": {
+ "args": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container start command arguments."
+ }
+ },
+ "command": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container start command."
+ }
+ },
+ "env": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/environmentVarType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container environment variables."
+ }
+ },
+ "image": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Container image tag."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Custom container name."
+ }
+ },
+ "probes": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/containerAppProbeType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of probes for the container."
+ }
+ },
+ "resources": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. Container resource requirements."
+ }
+ },
+ "volumeMounts": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/volumeMountType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Container volume mounts."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a container."
+ }
+ },
+ "ingressPortMappingType": {
+ "type": "object",
+ "properties": {
+ "exposedPort": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the exposed port for the target port. If not specified, it defaults to target port."
+ }
+ },
+ "external": {
+ "type": "bool",
+ "metadata": {
+ "description": "Required. Specifies whether the app port is accessible outside of the environment."
+ }
+ },
+ "targetPort": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. Specifies the port the container listens on."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an ingress port mapping."
+ }
+ },
+ "serviceBindingType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the service."
+ }
+ },
+ "serviceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The service ID."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a service binding."
+ }
+ },
+ "environmentVarType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Environment variable name."
+ }
+ },
+ "secretRef": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the Container App secret from which to pull the environment variable value."
+ }
+ },
+ "value": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Non-secret environment variable value."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an environment variable."
+ }
+ },
+ "containerAppProbeType": {
+ "type": "object",
+ "properties": {
+ "failureThreshold": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 10,
+ "metadata": {
+ "description": "Optional. Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3."
+ }
+ },
+ "httpGet": {
+ "$ref": "#/definitions/containerAppProbeHttpGetType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. HTTPGet specifies the http request to perform."
+ }
+ },
+ "initialDelaySeconds": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 60,
+ "metadata": {
+ "description": "Optional. Number of seconds after the container has started before liveness probes are initiated."
+ }
+ },
+ "periodSeconds": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 240,
+ "metadata": {
+ "description": "Optional. How often (in seconds) to perform the probe. Default to 10 seconds."
+ }
+ },
+ "successThreshold": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 10,
+ "metadata": {
+ "description": "Optional. Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup."
+ }
+ },
+ "tcpSocket": {
+ "$ref": "#/definitions/containerAppProbeTcpSocketType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The TCP socket specifies an action involving a TCP port. TCP hooks not yet supported."
+ }
+ },
+ "terminationGracePeriodSeconds": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate. Maximum value is 3600 seconds (1 hour)."
+ }
+ },
+ "timeoutSeconds": {
+ "type": "int",
+ "nullable": true,
+ "minValue": 1,
+ "maxValue": 240,
+ "metadata": {
+ "description": "Optional. Number of seconds after which the probe times out. Defaults to 1 second."
+ }
+ },
+ "type": {
+ "type": "string",
+ "allowedValues": [
+ "Liveness",
+ "Readiness",
+ "Startup"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The type of probe."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe."
+ }
+ },
+ "corsPolicyType": {
+ "type": "object",
+ "properties": {
+ "allowCredentials": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Switch to determine whether the resource allows credentials."
+ }
+ },
+ "allowedHeaders": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-allow-headers header."
+ }
+ },
+ "allowedMethods": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-allow-methods header."
+ }
+ },
+ "allowedOrigins": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-allow-origins header."
+ }
+ },
+ "exposeHeaders": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-expose-headers header."
+ }
+ },
+ "maxAge": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the content for the access-control-max-age header."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a CORS policy."
+ }
+ },
+ "containerAppProbeHttpGetType": {
+ "type": "object",
+ "properties": {
+ "host": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Host name to connect to. Defaults to the pod IP."
+ }
+ },
+ "httpHeaders": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/containerAppProbeHttpGetHeadersItemType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. HTTP headers to set in the request."
+ }
+ },
+ "path": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Path to access on the HTTP server."
+ }
+ },
+ "port": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. Name or number of the port to access on the container."
+ }
+ },
+ "scheme": {
+ "type": "string",
+ "allowedValues": [
+ "HTTP",
+ "HTTPS"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Scheme to use for connecting to the host. Defaults to HTTP."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe HTTP GET."
+ }
+ },
+ "containerAppProbeHttpGetHeadersItemType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the header."
+ }
+ },
+ "value": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Value of the header."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe HTTP GET header."
+ }
+ },
+ "containerAppProbeTcpSocketType": {
+ "type": "object",
+ "properties": {
+ "host": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Host name to connect to, defaults to the pod IP."
+ }
+ },
+ "port": {
+ "type": "int",
+ "minValue": 1,
+ "maxValue": 65535,
+ "metadata": {
+ "description": "Required. Number of the port to access on the container. Name must be an IANA_SVC_NAME."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a container app probe TCP socket."
+ }
+ },
+ "scaleType": {
+ "type": "object",
+ "properties": {
+ "maxReplicas": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. The maximum number of replicas."
+ }
+ },
+ "minReplicas": {
+ "type": "int",
+ "metadata": {
+ "description": "Required. The minimum number of replicas."
+ }
+ },
+ "cooldownPeriod": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The cooldown period in seconds."
+ }
+ },
+ "pollingInterval": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The polling interval in seconds."
+ }
+ },
+ "rules": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/scaleRuleType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The scaling rules."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The scale settings for the Container App."
+ }
+ },
+ "scaleRuleType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the scaling rule."
+ }
+ },
+ "custom": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The custom scaling rule."
+ }
+ },
+ "azureQueue": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Azure Queue based scaling rule."
+ }
+ },
+ "http": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The HTTP requests based scaling rule."
+ }
+ },
+ "tcp": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The TCP based scaling rule."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The scaling rules for the Container App."
+ }
+ },
+ "volumeMountType": {
+ "type": "object",
+ "properties": {
+ "mountPath": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Path within the container at which the volume should be mounted.Must not contain ':'."
+ }
+ },
+ "subPath": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root)."
+ }
+ },
+ "volumeName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. This must match the Name of a Volume."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a volume mount."
+ }
+ },
+ "secretType": {
+ "type": "object",
+ "properties": {
+ "identity": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity."
+ }
+ },
+ "keyVaultUrl": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The URL of the Azure Key Vault secret referenced by the Container App. Required if `value` is null."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the container app secret."
+ }
+ },
+ "value": {
+ "type": "securestring",
+ "nullable": true,
+ "metadata": {
+ "description": "Conditional. The container app secret value, if not fetched from the Key Vault. Required if `keyVaultUrl` is not null."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a secret."
+ }
+ },
+ "authConfigType": {
+ "type": "object",
+ "properties": {
+ "encryptionSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/encryptionSettings"
+ },
+ "description": "Optional. The configuration settings of the secrets references of encryption key and signing key for ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "globalValidation": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/globalValidation"
+ },
+ "description": "Optional. The configuration settings that determines the validation flow of users using Service Authentication and/or Authorization."
+ },
+ "nullable": true
+ },
+ "httpSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/httpSettings"
+ },
+ "description": "Optional. The configuration settings of the HTTP requests for authentication and authorization requests made against ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "identityProviders": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/identityProviders"
+ },
+ "description": "Optional. The configuration settings of each of the identity providers used to configure ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "login": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/login"
+ },
+ "description": "Optional. The configuration settings of the login flow of users using ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "platform": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/platform"
+ },
+ "description": "Optional. The configuration settings of the platform of ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for the container app's authentication configuration."
+ }
+ },
+ "diagnosticSettingMetricsOnlyType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
+ }
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a diagnostic setting. To be used if only metrics are supported by the resource provider.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "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"
+ },
+ "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.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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Container App."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "disableIngress": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Bool to disable all ingress traffic for the container app."
+ }
+ },
+ "ingressExternal": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Bool indicating if the App exposes an external HTTP endpoint."
+ }
+ },
+ "clientCertificateMode": {
+ "type": "string",
+ "defaultValue": "ignore",
+ "allowedValues": [
+ "accept",
+ "ignore",
+ "require"
+ ],
+ "metadata": {
+ "description": "Optional. Client certificate mode for mTLS."
+ }
+ },
+ "corsPolicy": {
+ "$ref": "#/definitions/corsPolicyType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Object userd to configure CORS policy."
+ }
+ },
+ "stickySessionsAffinity": {
+ "type": "string",
+ "defaultValue": "none",
+ "allowedValues": [
+ "none",
+ "sticky"
+ ],
+ "metadata": {
+ "description": "Optional. Bool indicating if the Container App should enable session affinity."
+ }
+ },
+ "ingressTransport": {
+ "type": "string",
+ "defaultValue": "auto",
+ "allowedValues": [
+ "auto",
+ "http",
+ "http2",
+ "tcp"
+ ],
+ "metadata": {
+ "description": "Optional. Ingress transport protocol."
+ }
+ },
+ "service": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/service"
+ },
+ "description": "Optional. Dev ContainerApp service type."
+ },
+ "nullable": true
+ },
+ "includeAddOns": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Toggle to include the service configuration."
+ }
+ },
+ "additionalPortMappings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ingressPortMappingType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Settings to expose additional ports on container app."
+ }
+ },
+ "ingressAllowInsecure": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Bool indicating if HTTP connections to is allowed. If set to false HTTP connections are automatically redirected to HTTPS connections."
+ }
+ },
+ "ingressTargetPort": {
+ "type": "int",
+ "defaultValue": 80,
+ "metadata": {
+ "description": "Optional. Target Port in containers for traffic from ingress."
+ }
+ },
+ "scaleSettings": {
+ "$ref": "#/definitions/scaleType",
+ "defaultValue": {
+ "maxReplicas": 10,
+ "minReplicas": 3
+ },
+ "metadata": {
+ "description": "Optional. The scaling settings of the service."
+ }
+ },
+ "serviceBinds": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/serviceBindingType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of container app services bound to the app."
+ }
+ },
+ "activeRevisionsMode": {
+ "type": "string",
+ "defaultValue": "Single",
+ "allowedValues": [
+ "Multiple",
+ "Single"
+ ],
+ "metadata": {
+ "description": "Optional. Controls how active revisions are handled for the Container app."
+ }
+ },
+ "environmentResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of environment."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "registries": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/registries"
+ },
+ "description": "Optional. Collection of private container registry credentials for containers used by the Container app."
+ },
+ "nullable": true
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "customDomains": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/ingress/properties/customDomains"
+ },
+ "description": "Optional. Custom domain bindings for Container App hostnames."
+ },
+ "nullable": true
+ },
+ "exposedPort": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Exposed Port in containers for TCP traffic from ingress."
+ }
+ },
+ "ipSecurityRestrictions": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/ingress/properties/ipSecurityRestrictions"
+ },
+ "description": "Optional. Rules to restrict incoming IP address."
+ },
+ "nullable": true
+ },
+ "trafficLabel": {
+ "type": "string",
+ "defaultValue": "label-1",
+ "metadata": {
+ "description": "Optional. Associates a traffic label with a revision. Label name should be consist of lower case alphanumeric characters or dashes."
+ }
+ },
+ "trafficLatestRevision": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Indicates that the traffic weight belongs to a latest stable revision."
+ }
+ },
+ "trafficRevisionName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Name of a revision."
+ }
+ },
+ "trafficWeight": {
+ "type": "int",
+ "defaultValue": 100,
+ "metadata": {
+ "description": "Optional. Traffic weight assigned to a revision."
+ }
+ },
+ "dapr": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/dapr"
+ },
+ "description": "Optional. Dapr configuration for the Container App."
+ },
+ "nullable": true
+ },
+ "identitySettings": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/identitySettings"
+ },
+ "description": "Optional. Settings for Managed Identities that are assigned to the Container App. If a Managed Identity is not specified here, default settings will be used."
+ },
+ "nullable": true
+ },
+ "maxInactiveRevisions": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Max inactive revisions a Container App can have."
+ }
+ },
+ "runtime": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/configuration/properties/runtime"
+ },
+ "description": "Optional. Runtime configuration for the Container App."
+ },
+ "nullable": true
+ },
+ "containers": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/containerType"
+ },
+ "metadata": {
+ "description": "Required. List of container definitions for the Container App."
+ }
+ },
+ "initContainersTemplate": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/template/properties/initContainers"
+ },
+ "description": "Optional. List of specialized containers that run before app containers."
+ },
+ "nullable": true
+ },
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/secretType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The secrets of the Container App."
+ }
+ },
+ "revisionSuffix": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. User friendly suffix that is appended to the revision name."
+ }
+ },
+ "volumes": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps@2025-01-01#properties/properties/properties/template/properties/volumes"
+ },
+ "description": "Optional. List of volume definitions for the Container App."
+ },
+ "nullable": true
+ },
+ "workloadProfileName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Workload profile name to pin for container app execution."
+ }
+ },
+ "authConfig": {
+ "$ref": "#/definitions/authConfigType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the Container App Auth configs."
+ }
+ },
+ "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)))))]"
+ }
+ ],
+ "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": {
+ "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]",
+ "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.app-containerapp.{0}.{1}', replace('0.18.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"
+ }
+ }
+ }
+ }
+ },
+ "containerApp": {
+ "type": "Microsoft.App/containerApps",
+ "apiVersion": "2025-01-01",
+ "name": "[parameters('name')]",
+ "tags": "[parameters('tags')]",
+ "location": "[parameters('location')]",
+ "identity": "[variables('identity')]",
+ "properties": {
+ "environmentId": "[parameters('environmentResourceId')]",
+ "workloadProfileName": "[parameters('workloadProfileName')]",
+ "template": {
+ "containers": "[parameters('containers')]",
+ "initContainers": "[if(not(empty(parameters('initContainersTemplate'))), parameters('initContainersTemplate'), null())]",
+ "revisionSuffix": "[parameters('revisionSuffix')]",
+ "scale": "[parameters('scaleSettings')]",
+ "serviceBinds": "[if(and(parameters('includeAddOns'), not(empty(parameters('serviceBinds')))), parameters('serviceBinds'), null())]",
+ "volumes": "[if(not(empty(parameters('volumes'))), parameters('volumes'), null())]"
+ },
+ "configuration": {
+ "activeRevisionsMode": "[parameters('activeRevisionsMode')]",
+ "dapr": "[if(not(empty(parameters('dapr'))), parameters('dapr'), null())]",
+ "identitySettings": "[if(not(empty(parameters('identitySettings'))), parameters('identitySettings'), null())]",
+ "ingress": "[if(parameters('disableIngress'), null(), createObject('additionalPortMappings', parameters('additionalPortMappings'), 'allowInsecure', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('ingressAllowInsecure'), false()), 'customDomains', if(not(empty(parameters('customDomains'))), parameters('customDomains'), null()), 'corsPolicy', if(and(not(equals(parameters('corsPolicy'), null())), not(equals(parameters('ingressTransport'), 'tcp'))), createObject('allowCredentials', coalesce(tryGet(parameters('corsPolicy'), 'allowCredentials'), false()), 'allowedHeaders', coalesce(tryGet(parameters('corsPolicy'), 'allowedHeaders'), createArray()), 'allowedMethods', coalesce(tryGet(parameters('corsPolicy'), 'allowedMethods'), createArray()), 'allowedOrigins', coalesce(tryGet(parameters('corsPolicy'), 'allowedOrigins'), createArray()), 'exposeHeaders', coalesce(tryGet(parameters('corsPolicy'), 'exposeHeaders'), createArray()), 'maxAge', tryGet(parameters('corsPolicy'), 'maxAge')), null()), 'clientCertificateMode', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('clientCertificateMode'), null()), 'exposedPort', parameters('exposedPort'), 'external', parameters('ingressExternal'), 'ipSecurityRestrictions', if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null()), 'targetPort', parameters('ingressTargetPort'), 'stickySessions', createObject('affinity', parameters('stickySessionsAffinity')), 'traffic', if(not(equals(parameters('ingressTransport'), 'tcp')), createArray(createObject('label', parameters('trafficLabel'), 'latestRevision', parameters('trafficLatestRevision'), 'revisionName', parameters('trafficRevisionName'), 'weight', parameters('trafficWeight'))), null()), 'transport', parameters('ingressTransport')))]",
+ "service": "[if(and(parameters('includeAddOns'), not(empty(parameters('service')))), parameters('service'), null())]",
+ "maxInactiveRevisions": "[parameters('maxInactiveRevisions')]",
+ "registries": "[if(not(empty(parameters('registries'))), parameters('registries'), null())]",
+ "secrets": "[parameters('secrets')]",
+ "runtime": "[if(not(empty(parameters('runtime'))), parameters('runtime'), null())]"
+ }
+ }
+ },
+ "containerApp_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.App/containerApps/{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": [
+ "containerApp"
+ ]
+ },
+ "containerApp_roleAssignments": {
+ "copy": {
+ "name": "containerApp_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.App/containerApps/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/containerApps', 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": [
+ "containerApp"
+ ]
+ },
+ "containerApp_diagnosticSettings": {
+ "copy": {
+ "name": "containerApp_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.App/containerApps/{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": [
+ "containerApp"
+ ]
+ },
+ "containerAppAuthConfigs": {
+ "condition": "[not(empty(parameters('authConfig')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-auth-config', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "containerAppName": {
+ "value": "[parameters('name')]"
+ },
+ "encryptionSettings": {
+ "value": "[tryGet(parameters('authConfig'), 'encryptionSettings')]"
+ },
+ "globalValidation": {
+ "value": "[tryGet(parameters('authConfig'), 'globalValidation')]"
+ },
+ "httpSettings": {
+ "value": "[tryGet(parameters('authConfig'), 'httpSettings')]"
+ },
+ "identityProviders": {
+ "value": "[tryGet(parameters('authConfig'), 'identityProviders')]"
+ },
+ "login": {
+ "value": "[tryGet(parameters('authConfig'), 'login')]"
+ },
+ "platform": {
+ "value": "[tryGet(parameters('authConfig'), 'platform')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "9975390462196064744"
+ },
+ "name": "Container App Auth Configs",
+ "description": "This module deploys Container App Auth Configs."
+ },
+ "parameters": {
+ "containerAppName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Container App. Required if the template is used in a standalone deployment."
+ }
+ },
+ "encryptionSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/encryptionSettings"
+ },
+ "description": "Optional. The configuration settings of the secrets references of encryption key and signing key for ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "globalValidation": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/globalValidation"
+ },
+ "description": "Optional. The configuration settings that determines the validation flow of users using Service Authentication and/or Authorization."
+ },
+ "nullable": true
+ },
+ "httpSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/httpSettings"
+ },
+ "description": "Optional. The configuration settings of the HTTP requests for authentication and authorization requests made against ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "identityProviders": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/identityProviders"
+ },
+ "description": "Optional. The configuration settings of each of the identity providers used to configure ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "login": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/login"
+ },
+ "description": "Optional. The configuration settings of the login flow of users using ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ },
+ "platform": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.App/containerApps/authConfigs@2025-01-01#properties/properties/properties/platform"
+ },
+ "description": "Optional. The configuration settings of the platform of ContainerApp Service Authentication/Authorization."
+ },
+ "nullable": true
+ }
+ },
+ "resources": {
+ "containerApp": {
+ "existing": true,
+ "type": "Microsoft.App/containerApps",
+ "apiVersion": "2025-01-01",
+ "name": "[parameters('containerAppName')]"
+ },
+ "containerAppAuthConfigs": {
+ "type": "Microsoft.App/containerApps/authConfigs",
+ "apiVersion": "2025-01-01",
+ "name": "[format('{0}/{1}', parameters('containerAppName'), 'current')]",
+ "properties": {
+ "encryptionSettings": "[parameters('encryptionSettings')]",
+ "globalValidation": "[parameters('globalValidation')]",
+ "httpSettings": "[parameters('httpSettings')]",
+ "identityProviders": "[parameters('identityProviders')]",
+ "login": "[parameters('login')]",
+ "platform": "[parameters('platform')]"
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the set of Container App Auth configs."
+ },
+ "value": "current"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the set of Container App Auth configs."
+ },
+ "value": "[resourceId('Microsoft.App/containerApps/authConfigs', parameters('containerAppName'), 'current')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group containing the set of Container App Auth configs."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "containerApp"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Container App."
+ },
+ "value": "[resourceId('Microsoft.App/containerApps', parameters('name'))]"
+ },
+ "fqdn": {
+ "type": "string",
+ "metadata": {
+ "description": "The configuration of ingress fqdn."
+ },
+ "value": "[if(parameters('disableIngress'), 'IngressDisabled', reference('containerApp').configuration.ingress.fqdn)]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the Container App was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Container App."
+ },
+ "value": "[parameters('name')]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[tryGet(tryGet(reference('containerApp', '2025-01-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('containerApp', '2025-01-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "containerAppEnvironment",
+ "userAssignedIdentity"
+ ]
+ },
+ "webServerFarm": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.web.serverfarm.{0}', variables('webServerFarmResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('webServerFarmResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "reserved": {
+ "value": true
+ },
+ "kind": {
+ "value": "linux"
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]",
+ "skuName": "[if(or(parameters('enableScalability'), parameters('enableRedundancy')), createObject('value', 'P1v4'), createObject('value', 'B3'))]",
+ "skuCapacity": "[if(parameters('enableScalability'), createObject('value', 3), createObject('value', 1))]",
+ "zoneRedundant": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]"
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "16945786131371363466"
+ },
+ "name": "App Service Plan",
+ "description": "This module deploys an App Service Plan."
+ },
+ "definitions": {
+ "diagnosticSettingMetricsOnlyType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
+ }
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a diagnostic setting. To be used if only metrics are supported by the resource provider.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "notes": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the notes of the lock."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a lock.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "roleAssignmentType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated."
+ }
+ },
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a role assignment.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 60,
+ "metadata": {
+ "description": "Required. Name of the app service plan."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "defaultValue": "P1v3",
+ "metadata": {
+ "example": " 'F1'\n 'B1'\n 'P1v3'\n 'I1v2'\n 'FC1'\n ",
+ "description": "Optional. The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones."
+ }
+ },
+ "skuCapacity": {
+ "type": "int",
+ "defaultValue": 3,
+ "metadata": {
+ "description": "Optional. Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "defaultValue": "app",
+ "allowedValues": [
+ "app",
+ "elastic",
+ "functionapp",
+ "windows",
+ "linux"
+ ],
+ "metadata": {
+ "description": "Optional. Kind of server OS."
+ }
+ },
+ "reserved": {
+ "type": "bool",
+ "defaultValue": "[equals(parameters('kind'), 'linux')]",
+ "metadata": {
+ "description": "Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true."
+ }
+ },
+ "appServiceEnvironmentResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The Resource ID of the App Service Environment to use for the App Service Plan."
+ }
+ },
+ "workerTierName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Target worker tier assigned to the App Service plan."
+ }
+ },
+ "perSiteScaling": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. If true, apps assigned to this App Service plan can be scaled independently. If false, apps assigned to this App Service plan will scale to all instances of the plan."
+ }
+ },
+ "elasticScaleEnabled": {
+ "type": "bool",
+ "defaultValue": "[greater(parameters('maximumElasticWorkerCount'), 1)]",
+ "metadata": {
+ "description": "Optional. Enable/Disable ElasticScaleEnabled App Service Plan."
+ }
+ },
+ "maximumElasticWorkerCount": {
+ "type": "int",
+ "defaultValue": 1,
+ "metadata": {
+ "description": "Optional. Maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan."
+ }
+ },
+ "targetWorkerCount": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Scaling worker count."
+ }
+ },
+ "targetWorkerSize": {
+ "type": "int",
+ "defaultValue": 0,
+ "allowedValues": [
+ 0,
+ 1,
+ 2
+ ],
+ "metadata": {
+ "description": "Optional. The instance size of the hosting plan (small, medium, or large)."
+ }
+ },
+ "zoneRedundant": {
+ "type": "bool",
+ "defaultValue": "[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]",
+ "metadata": {
+ "description": "Optional. Zone Redundant server farms can only be used on Premium or ElasticPremium SKU tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs)."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Web/serverfarms@2024-11-01#properties/tags"
+ },
+ "description": "Optional. Tags of the resource."
+ },
+ "nullable": true
+ },
+ "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.5.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"
+ }
+ }
+ }
+ }
+ },
+ "appServicePlan": {
+ "type": "Microsoft.Web/serverfarms",
+ "apiVersion": "2024-11-01",
+ "name": "[parameters('name')]",
+ "kind": "[parameters('kind')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "sku": "[if(equals(parameters('skuName'), 'FC1'), createObject('name', parameters('skuName'), 'tier', 'FlexConsumption'), createObject('name', parameters('skuName'), 'capacity', parameters('skuCapacity')))]",
+ "properties": {
+ "workerTierName": "[parameters('workerTierName')]",
+ "hostingEnvironmentProfile": "[if(not(empty(parameters('appServiceEnvironmentResourceId'))), createObject('id', parameters('appServiceEnvironmentResourceId')), 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": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]"
+ },
+ "dependsOn": [
+ "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": {
+ "description": "The resource group the app service plan was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the app service plan."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the app service plan."
+ },
+ "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('appServicePlan', '2024-11-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "logAnalyticsWorkspace"
+ ]
+ },
+ "webSite": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('module.web-sites.{0}', variables('webSiteResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('webSiteResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "kind": {
+ "value": "app,linux,container"
+ },
+ "serverFarmResourceId": {
+ "value": "[tryGet(reference('webServerFarm'), 'outputs', 'resourceId', 'value')]"
+ },
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": true
+ }
+ },
+ "siteConfig": {
+ "value": {
+ "linuxFxVersion": "[format('DOCKER|{0}/{1}:{2}', parameters('frontendContainerRegistryHostname'), parameters('frontendContainerImageName'), parameters('frontendContainerImageTag'))]",
+ "minTlsVersion": "1.2"
+ }
+ },
+ "configs": {
+ "value": [
+ {
+ "name": "appsettings",
+ "properties": {
+ "SCM_DO_BUILD_DURING_DEPLOYMENT": "true",
+ "DOCKER_REGISTRY_SERVER_URL": "[format('https://{0}', parameters('frontendContainerRegistryHostname'))]",
+ "WEBSITES_PORT": "3000",
+ "WEBSITES_CONTAINER_START_TIME_LIMIT": "1800",
+ "BACKEND_API_URL": "[format('https://{0}', reference('containerApp').outputs.fqdn.value)]",
+ "AUTH_ENABLED": "false"
+ },
+ "applicationInsightResourceId": "[if(parameters('enableMonitoring'), reference('applicationInsights').outputs.resourceId.value, null())]"
+ }
+ ]
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]",
+ "vnetRouteAllEnabled": "[if(parameters('enablePrivateNetworking'), createObject('value', true()), createObject('value', false()))]",
+ "vnetImagePullEnabled": "[if(parameters('enablePrivateNetworking'), createObject('value', true()), createObject('value', false()))]",
+ "virtualNetworkSubnetId": "[if(parameters('enablePrivateNetworking'), createObject('value', reference('virtualNetwork').outputs.webserverfarmSubnetResourceId.value), createObject('value', null()))]",
+ "publicNetworkAccess": {
+ "value": "Enabled"
+ },
+ "e2eEncryptionEnabled": {
+ "value": 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.41.2.15936",
+ "templateHash": "14525082674956141939"
+ }
+ },
+ "definitions": {
+ "appSettingsConfigType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "allowedValues": [
+ "appsettings"
+ ],
+ "metadata": {
+ "description": "Required. The type of config."
+ }
+ },
+ "storageAccountUseIdentityAuthentication": {
+ "type": "bool",
+ "nullable": true,
+ "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'."
+ }
+ },
+ "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."
+ }
+ },
+ "applicationInsightResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the application insight to leverage for this resource."
+ }
+ },
+ "retainCurrentAppSettings": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The retain the current app settings. Defaults to true."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. An app settings key-value pair."
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type of an app settings configuration."
+ }
+ },
+ "_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"
+ }
+ }
+ },
+ "_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.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"
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "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/_1.lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.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"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the site."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "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."
+ }
+ },
+ "serverFarmResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the app service plan to use for the site."
+ }
+ },
+ "managedEnvironmentId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Azure Resource Manager ID of the customers selected Managed Environment on which to host this app."
+ }
+ },
+ "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": "Optional. The resource ID of the app service environment to use for this resource."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentityAllType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "keyVaultAccessIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID of the assigned identity to be used to access a key vault with."
+ }
+ },
+ "storageAccountRequired": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Checks if Customer provided storage account is required."
+ }
+ },
+ "virtualNetworkSubnetId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "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}."
+ }
+ },
+ "vnetContentShareEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "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."
+ }
+ },
+ "scmSiteAlsoStopped": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Stop SCM (KUDU) site when the app is stopped."
+ }
+ },
+ "siteConfig": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Web/sites@2024-04-01#properties/properties/properties/siteConfig"
+ },
+ "description": "Optional. The site config object. The defaults are set to the following values: alwaysOn: true, minTlsVersion: '1.2', ftpsState: 'FtpsOnly'."
+ },
+ "defaultValue": {
+ "alwaysOn": true,
+ "minTlsVersion": "1.2",
+ "ftpsState": "FtpsOnly"
+ }
+ },
+ "configs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/appSettingsConfigType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The web site config."
+ }
+ },
+ "functionAppConfig": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Web/sites@2024-04-01#properties/properties/properties/functionAppConfig"
+ },
+ "description": "Optional. The Function App configuration object."
+ },
+ "nullable": true
+ },
+ "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. Tags of the resource."
+ }
+ },
+ "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": "Optional. Client certificate authentication comma-separated exclusion paths."
+ }
+ },
+ "clientCertMode": {
+ "type": "string",
+ "defaultValue": "Optional",
+ "allowedValues": [
+ "Optional",
+ "OptionalInteractiveUser",
+ "Required"
+ ],
+ "metadata": {
+ "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"
+ }
+ },
+ "cloningInfo": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Web/sites@2024-04-01#properties/properties/properties/cloningInfo"
+ },
+ "description": "Optional. If specified during app creation, the app is cloned from a source app."
+ },
+ "nullable": true
+ },
+ "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)."
+ }
+ },
+ "hostNameSslStates": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Web/sites@2024-04-01#properties/properties/properties/hostNameSslStates"
+ },
+ "description": "Optional. Hostname SSL states are used to manage the SSL bindings for app's hostnames."
+ },
+ "nullable": true
+ },
+ "hyperV": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Hyper-V sandbox."
+ }
+ },
+ "redundancyMode": {
+ "type": "string",
+ "defaultValue": "None",
+ "allowedValues": [
+ "ActiveActive",
+ "Failover",
+ "GeoRedundant",
+ "Manual",
+ "None"
+ ],
+ "metadata": {
+ "description": "Optional. Site redundancy mode."
+ }
+ },
+ "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."
+ }
+ },
+ "dnsConfiguration": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Web/sites@2024-04-01#properties/properties/properties/dnsConfiguration"
+ },
+ "description": "Optional. Property to configure various DNS related settings for a site."
+ },
+ "nullable": true
+ },
+ "autoGeneratedDomainNameLabelScope": {
+ "type": "string",
+ "nullable": true,
+ "allowedValues": [
+ "NoReuse",
+ "ResourceGroupReuse",
+ "SubscriptionReuse",
+ "TenantReuse"
+ ],
+ "metadata": {
+ "description": "Optional. Specifies the scope of uniqueness for the default hostname during resource creation."
+ }
+ }
+ },
+ "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', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]"
+ },
+ "resources": {
+ "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')]",
+ "dnsConfiguration": "[parameters('dnsConfiguration')]",
+ "autoGeneratedDomainNameLabelScope": "[parameters('autoGeneratedDomainNameLabelScope')]"
+ }
+ },
+ "app_diagnosticSettings": {
+ "copy": {
+ "name": "app_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[resourceId('Microsoft.Web/sites', 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": [
+ "app"
+ ]
+ },
+ "app_config": {
+ "copy": {
+ "name": "app_config",
+ "count": "[length(coalesce(parameters('configs'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[format('{0}-Site-Config-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "appName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('configs'), createArray())[copyIndex()].name]"
+ },
+ "applicationInsightResourceId": {
+ "value": "[tryGet(coalesce(parameters('configs'), createArray())[copyIndex()], 'applicationInsightResourceId')]"
+ },
+ "storageAccountResourceId": {
+ "value": "[tryGet(coalesce(parameters('configs'), createArray())[copyIndex()], 'storageAccountResourceId')]"
+ },
+ "storageAccountUseIdentityAuthentication": {
+ "value": "[tryGet(coalesce(parameters('configs'), createArray())[copyIndex()], 'storageAccountUseIdentityAuthentication')]"
+ },
+ "properties": {
+ "value": "[tryGet(coalesce(parameters('configs'), createArray())[copyIndex()], 'properties')]"
+ },
+ "currentAppSettings": "[if(coalesce(tryGet(coalesce(parameters('configs'), createArray())[copyIndex()], 'retainCurrentAppSettings'), and(true(), equals(coalesce(parameters('configs'), createArray())[copyIndex()].name, 'appsettings'))), 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.41.2.15936",
+ "templateHash": "1185169597469996118"
+ },
+ "name": "Site App Settings",
+ "description": "This module deploys a Site App Setting."
+ },
+ "parameters": {
+ "appName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "allowedValues": [
+ "appsettings",
+ "authsettings",
+ "authsettingsV2",
+ "azurestorageaccounts",
+ "backup",
+ "connectionstrings",
+ "logs",
+ "metadata",
+ "pushsettings",
+ "slotConfigNames",
+ "web"
+ ],
+ "metadata": {
+ "description": "Required. The name of the config."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The properties of the config. Note: This parameter is highly dependent on the config type, defined by its name."
+ }
+ },
+ "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'."
+ }
+ },
+ "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."
+ }
+ },
+ "applicationInsightResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the application insight to leverage for this resource."
+ }
+ },
+ "currentAppSettings": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The key-values pairs of the current app settings."
+ }
+ },
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The current app settings."
+ }
+ }
+ },
+ "resources": {
+ "applicationInsights": {
+ "condition": "[not(empty(parameters('applicationInsightResourceId')))]",
+ "existing": true,
+ "type": "Microsoft.Insights/components",
+ "apiVersion": "2020-02-02",
+ "subscriptionId": "[split(parameters('applicationInsightResourceId'), '/')[2]]",
+ "resourceGroup": "[split(parameters('applicationInsightResourceId'), '/')[4]]",
+ "name": "[last(split(parameters('applicationInsightResourceId'), '/'))]"
+ },
+ "storageAccount": {
+ "condition": "[not(empty(parameters('storageAccountResourceId')))]",
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "subscriptionId": "[split(parameters('storageAccountResourceId'), '/')[2]]",
+ "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]",
+ "name": "[last(split(parameters('storageAccountResourceId'), '/'))]"
+ },
+ "app": {
+ "existing": true,
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2023-12-01",
+ "name": "[parameters('appName')]"
+ },
+ "config": {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2024-04-01",
+ "name": "[format('{0}/{1}', parameters('appName'), parameters('name'))]",
+ "properties": "[union(parameters('currentAppSettings'), parameters('properties'), 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('storageAccount', '2024-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), createObject('AzureWebJobsStorage__accountName', last(split(parameters('storageAccountResourceId'), '/')), 'AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob, 'AzureWebJobsStorage__queueServiceUri', reference('storageAccount').primaryEndpoints.queue, 'AzureWebJobsStorage__tableServiceUri', reference('storageAccount').primaryEndpoints.table), createObject())), if(not(empty(parameters('applicationInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('applicationInsights').ConnectionString), createObject()))]",
+ "dependsOn": [
+ "applicationInsights",
+ "storageAccount"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the site config."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the site config."
+ },
+ "value": "[resourceId('Microsoft.Web/sites/config', parameters('appName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the site config was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "app"
+ ]
+ },
+ "app_privateEndpoints": {
+ "copy": {
+ "name": "app_privateEndpoints",
+ "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[format('{0}-app-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]",
+ "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]",
+ "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.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), 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.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]"
+ },
+ "enableTelemetry": {
+ "value": false
+ },
+ "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'), null())]"
+ },
+ "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": [
+ "app"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the site."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the site."
+ },
+ "value": "[resourceId('Microsoft.Web/sites', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the site 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('app', '2024-04-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('app', '2024-04-01', 'full').location]"
+ },
+ "defaultHostname": {
+ "type": "string",
+ "metadata": {
+ "description": "Default hostname of the app."
+ },
+ "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]"
+ },
+ "outboundIpAddresses": {
+ "type": "string",
+ "metadata": {
+ "description": "The outbound IP addresses of the app."
+ },
+ "value": "[reference('app').outboundIpAddresses]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "applicationInsights",
+ "containerApp",
+ "logAnalyticsWorkspace",
+ "virtualNetwork",
+ "webServerFarm"
+ ]
+ },
+ "avmStorageAccount": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.storage.storage-account.{0}', variables('storageAccountName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('storageAccountName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": true
+ }
+ },
+ "minimumTlsVersion": {
+ "value": "TLS1_2"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "accessTier": {
+ "value": "Hot"
+ },
+ "supportsHttpsTrafficOnly": {
+ "value": true
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "roleDefinitionIdOrName": "Storage Blob Data Contributor",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "principalId": "[variables('deployingUserPrincipalId')]",
+ "roleDefinitionIdOrName": "Storage Blob Data Contributor",
+ "principalType": "[variables('deployerPrincipalType')]"
+ }
+ ]
+ },
+ "networkAcls": {
+ "value": {
+ "bypass": "AzureServices",
+ "defaultAction": "[if(parameters('enablePrivateNetworking'), 'Deny', 'Allow')]"
+ }
+ },
+ "allowBlobPublicAccess": {
+ "value": false
+ },
+ "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]",
+ "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-blob-{0}', variables('solutionSuffix')), 'customNetworkInterfaceName', format('nic-blob-{0}', variables('solutionSuffix')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'storage-dns-zone-group-blob', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').blob)).outputs.resourceId.value))), 'subnetResourceId', reference('virtualNetwork').outputs.backendSubnetResourceId.value, 'service', 'blob'))), createObject('value', createArray()))]",
+ "blobServices": {
+ "value": {
+ "automaticSnapshotPolicyEnabled": true,
+ "containerDeleteRetentionPolicyDays": 10,
+ "containerDeleteRetentionPolicyEnabled": true,
+ "containers": [
+ {
+ "name": "[parameters('storageContainerNameRetailCustomer')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameRetailOrder')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameRFPSummary')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameRFPRisk')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameRFPCompliance')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameContractSummary')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameContractRisk')]",
+ "publicAccess": "None"
+ },
+ {
+ "name": "[parameters('storageContainerNameContractCompliance')]",
+ "publicAccess": "None"
+ }
+ ],
+ "deleteRetentionPolicyDays": 9,
+ "deleteRetentionPolicyEnabled": true,
+ "lastAccessTimeTrackingPolicyEnabled": 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.35.1.17967",
+ "templateHash": "13086360467000063396"
+ },
+ "name": "Storage Accounts",
+ "description": "This module deploys a Storage Account."
+ },
+ "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
+ }
+ },
+ "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."
+ }
+ },
+ "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."
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "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_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"
+ },
+ "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 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."
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "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": "Required. Name of the Storage Account. Must be lower-case."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "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": {
+ "type": "string",
+ "defaultValue": "StorageV2",
+ "allowedValues": [
+ "Storage",
+ "StorageV2",
+ "BlobStorage",
+ "FileStorage",
+ "BlockBlobStorage"
+ ],
+ "metadata": {
+ "description": "Optional. Type of Storage Account to create."
+ }
+ },
+ "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": "Optional. Storage Account Sku Name."
+ }
+ },
+ "accessTier": {
+ "type": "string",
+ "defaultValue": "Hot",
+ "allowedValues": [
+ "Premium",
+ "Hot",
+ "Cool",
+ "Cold"
+ ],
+ "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."
+ }
+ },
+ "largeFileSharesState": {
+ "type": "string",
+ "defaultValue": "Disabled",
+ "allowedValues": [
+ "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",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts@2024-01-01#properties/properties/properties/azureFilesIdentityBasedAuthentication"
+ },
+ "description": "Optional. Provides the identity based authentication settings for Azure Files."
+ },
+ "nullable": true
+ },
+ "defaultToOAuthAuthentication": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not."
+ }
+ },
+ "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": {
+ "$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."
+ }
+ },
+ "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": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source."
+ }
+ },
+ "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",
+ "nullable": true,
+ "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"
+ ],
+ "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."
+ }
+ },
+ "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/localUserType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Local users to deploy for SFTP authentication."
+ }
+ },
+ "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/diagnosticSettingFullType"
+ },
+ "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."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts@2024-01-01#properties/tags"
+ },
+ "description": "Optional. Tags of the resource."
+ },
+ "nullable": true
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "allowedCopyScope": {
+ "type": "string",
+ "nullable": true,
+ "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",
+ "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."
+ }
+ },
+ "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."
+ }
+ },
+ "sasExpirationAction": {
+ "type": "string",
+ "defaultValue": "Log",
+ "allowedValues": [
+ "Block",
+ "Log"
+ ],
+ "metadata": {
+ "description": "Optional. The SAS expiration action. Allowed values are Block and Log."
+ }
+ },
+ "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)))))]"
+ }
+ ],
+ "enableReferencedModulesTelemetry": false,
+ "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": "2024-11-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.storage-storageaccount.{0}.{1}', replace('0.20.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": "2024-11-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": "2024-11-30",
+ "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]",
+ "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]",
+ "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]"
+ },
+ "storageAccount": {
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "kind": "[parameters('kind')]",
+ "sku": {
+ "name": "[parameters('skuName')]"
+ },
+ "identity": "[variables('identity')]",
+ "tags": "[parameters('tags')]",
+ "properties": "[shallowMerge(createArray(createObject('allowSharedKeyAccess', parameters('allowSharedKeyAccess'), 'defaultToOAuthAuthentication', parameters('defaultToOAuthAuthentication'), 'allowCrossTenantReplication', parameters('allowCrossTenantReplication'), 'allowedCopyScope', parameters('allowedCopyScope'), 'customDomain', createObject('name', parameters('customDomainName'), 'useSubDomainName', parameters('customDomainUseSubDomainName')), 'dnsEndpointType', parameters('dnsEndpointType'), '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(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2], split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))), 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', parameters('sasExpirationAction'), '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()))), if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), createObject('azureFilesIdentityBasedAuthentication', parameters('azureFilesIdentityBasedAuthentication')), createObject())))]",
+ "dependsOn": [
+ "cMKKeyVault",
+ "cMKKeyVault::cMKKey"
+ ]
+ },
+ "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": "[format('{0}-sa-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.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": "[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": [
+ "storageAccount"
+ ]
+ },
+ "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": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "storageAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "rules": {
+ "value": "[parameters('managementPolicyRules')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "11585123047105458062"
+ },
+ "name": "Storage Account Management Policies",
+ "description": "This module deploys a Storage Account Management 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."
+ }
+ },
+ "rules": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts/managementPolicies@2024-01-01#properties/properties/properties/policy/properties/rules"
+ },
+ "description": "Required. The Storage Account ManagementPolicies Rules."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Storage/storageAccounts/managementPolicies",
+ "apiVersion": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]",
+ "properties": {
+ "policy": {
+ "rules": "[parameters('rules')]"
+ }
+ }
+ }
+ ],
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed management policy."
+ },
+ "value": "default"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed management policy."
+ },
+ "value": "default"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group of the deployed management policy."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "storageAccount",
+ "storageAccount_blobServices"
+ ]
+ },
+ "storageAccount_localUsers": {
+ "copy": {
+ "name": "storageAccount_localUsers",
+ "count": "[length(coalesce(parameters('localUsers'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "storageAccountName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].name]"
+ },
+ "hasSshKey": {
+ "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshKey]"
+ },
+ "hasSshPassword": {
+ "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshPassword]"
+ },
+ "permissionScopes": {
+ "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].permissionScopes]"
+ },
+ "hasSharedKey": {
+ "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'hasSharedKey')]"
+ },
+ "homeDirectory": {
+ "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'homeDirectory')]"
+ },
+ "sshAuthorizedKeys": {
+ "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'sshAuthorizedKeys')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "18350684375691178826"
+ },
+ "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
+ }
+ }
+ },
+ "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 local user used for SFTP Authentication."
+ }
+ },
+ "hasSharedKey": {
+ "type": "bool",
+ "defaultValue": false,
+ "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",
+ "defaultValue": "",
+ "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."
+ }
+ }
+ },
+ "resources": {
+ "storageAccount": {
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "localUsers": {
+ "type": "Microsoft.Storage/storageAccounts/localUsers",
+ "apiVersion": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]",
+ "properties": {
+ "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": "The resource group of the deployed local user."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "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.35.1.17967",
+ "templateHash": "6864791231608714221"
+ },
+ "name": "Storage Account blob Services",
+ "description": "This module deploys a Storage Account Blob 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": {
+ "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."
+ }
+ },
+ "automaticSnapshotPolicyEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Automatic Snapshot is enabled if set to true."
+ }
+ },
+ "changeFeedEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service."
+ }
+ },
+ "changeFeedRetentionInDays": {
+ "type": "int",
+ "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."
+ }
+ },
+ "containerDeleteRetentionPolicyDays": {
+ "type": "int",
+ "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."
+ }
+ },
+ "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."
+ }
+ },
+ "defaultServiceVersion": {
+ "type": "string",
+ "nullable": true,
+ "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."
+ }
+ },
+ "deleteRetentionPolicyDays": {
+ "type": "int",
+ "defaultValue": 7,
+ "minValue": 1,
+ "maxValue": 365,
+ "metadata": {
+ "description": "Optional. Indicates the number of days that the deleted blob should be retained."
+ }
+ },
+ "deleteRetentionPolicyAllowPermanentDelete": {
+ "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."
+ }
+ },
+ "isVersioningEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Use versioning to automatically maintain previous versions of your blobs."
+ }
+ },
+ "lastAccessTimeTrackingPolicyEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "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."
+ }
+ },
+ "restorePolicyEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "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."
+ }
+ },
+ "restorePolicyDays": {
+ "type": "int",
+ "defaultValue": 7,
+ "minValue": 1,
+ "metadata": {
+ "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days."
+ }
+ },
+ "containers": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Blob containers to create."
+ }
+ },
+ "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": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "blobServices": {
+ "type": "Microsoft.Storage/storageAccounts/blobServices",
+ "apiVersion": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]",
+ "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": "[parameters('defaultServiceVersion')]",
+ "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', '2024-01-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"
+ ]
+ },
+ "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')))]",
+ "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": [
+ "blobServices"
+ ]
+ },
+ "blobServices_container": {
+ "copy": {
+ "name": "blobServices_container",
+ "count": "[length(coalesce(parameters('containers'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]",
+ "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')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]"
+ },
+ "immutabilityPolicyProperties": {
+ "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "16608863835956278253"
+ },
+ "name": "Storage Account Blob Containers",
+ "description": "This module deploys a Storage Account Blob Container."
+ },
+ "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."
+ }
+ },
+ "blobServiceName": {
+ "type": "string",
+ "defaultValue": "default",
+ "metadata": {
+ "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the storage container to deploy."
+ }
+ },
+ "defaultEncryptionScope": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Default the container to use specified encryption scope for all writes."
+ }
+ },
+ "denyEncryptionScopeOverride": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Block override of encryption scope from the container default."
+ }
+ },
+ "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",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts/blobServices/containers@2024-01-01#properties/properties/properties/metadata"
+ },
+ "description": "Optional. A name-value pair to associate with the container as metadata."
+ },
+ "defaultValue": {}
+ },
+ "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": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]"
+ },
+ "storageAccount": {
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "container": {
+ "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
+ "apiVersion": "2024-01-01",
+ "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]",
+ "properties": {
+ "defaultEncryptionScope": "[parameters('defaultEncryptionScope')]",
+ "denyEncryptionScopeOverride": "[parameters('denyEncryptionScopeOverride')]",
+ "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')]"
+ }
+ },
+ "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.35.1.17967",
+ "templateHash": "16507112099495773673"
+ },
+ "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": "2024-01-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."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed container."
+ },
+ "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group of the deployed container."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "blobServices"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed blob service."
+ },
+ "value": "[variables('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed blob service."
+ },
+ "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the deployed blob service."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "storageAccount"
+ ]
+ },
+ "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.35.1.17967",
+ "templateHash": "16585885324390135986"
+ },
+ "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": {
+ "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."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "default",
+ "metadata": {
+ "description": "Optional. The name of the file service."
+ }
+ },
+ "protocolSettings": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts/fileServices@2024-01-01#properties/properties/properties/protocolSettings"
+ },
+ "description": "Optional. Protocol settings for file service."
+ },
+ "defaultValue": {}
+ },
+ "shareDeleteRetentionPolicy": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts/fileServices@2024-01-01#properties/properties/properties/shareDeleteRetentionPolicy"
+ },
+ "description": "Optional. The service properties for soft delete."
+ },
+ "defaultValue": {
+ "enabled": true,
+ "days": 7
+ }
+ },
+ "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": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "fileServices": {
+ "type": "Microsoft.Storage/storageAccounts/fileServices",
+ "apiVersion": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]",
+ "properties": {
+ "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"
+ },
+ "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', '2024-01-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')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "190690872747761309"
+ },
+ "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": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]"
+ },
+ "storageAccount": {
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "fileShare": {
+ "type": "Microsoft.Storage/storageAccounts/fileServices/shares",
+ "apiVersion": "2024-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]"
+ }
+ }
+ }
+ },
+ "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]"
+ }
+ }
+ }
+ },
+ "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.35.1.17967",
+ "templateHash": "15089132876669102729"
+ },
+ "name": "Storage Account Queue Services",
+ "description": "This module deploys a Storage Account Queue 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": {
+ "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": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "queueServices": {
+ "type": "Microsoft.Storage/storageAccounts/queueServices",
+ "apiVersion": "2024-01-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": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "9203389950224823099"
+ },
+ "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",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Storage/storageAccounts/queueServices/queues@2024-01-01#properties/properties/properties/metadata"
+ },
+ "description": "Optional. A name-value pair that represents queue metadata."
+ },
+ "defaultValue": {}
+ },
+ "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": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]"
+ },
+ "storageAccount": {
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "queue": {
+ "type": "Microsoft.Storage/storageAccounts/queueServices/queues",
+ "apiVersion": "2024-01-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."
+ },
+ "value": "[variables('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed file share service."
+ },
+ "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group of the deployed file share service."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "storageAccount"
+ ]
+ },
+ "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": {
+ "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')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.35.1.17967",
+ "templateHash": "17345564162551993063"
+ },
+ "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": {
+ "__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."
+ }
+ },
+ "tables": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. tables 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": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "tableServices": {
+ "type": "Microsoft.Storage/storageAccounts/tableServices",
+ "apiVersion": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]",
+ "properties": {
+ "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]"
+ }
+ },
+ "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": {
+ "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": [
+ "tableServices"
+ ]
+ },
+ "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.35.1.17967",
+ "templateHash": "6286190839827082273"
+ },
+ "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": "2024-01-01",
+ "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]"
+ },
+ "storageAccount": {
+ "existing": true,
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2024-01-01",
+ "name": "[parameters('storageAccountName')]"
+ },
+ "table": {
+ "type": "Microsoft.Storage/storageAccounts/tableServices/tables",
+ "apiVersion": "2024-01-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."
+ },
+ "value": "[variables('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the deployed table service."
+ },
+ "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]"
+ },
+ "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(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('storageAccount', '2024-01-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('storageAccount', '2024-01-01').keys[0].value, environment().suffixes.storage))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('storageAccount', '2024-01-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('storageAccount', '2024-01-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.35.1.17967",
+ "templateHash": "15126360152170162999"
+ }
+ },
+ "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": "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]"
+ }
+ }
+ },
+ "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": [
+ "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', '2024-01-01', 'full'), 'identity'), 'principalId')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('storageAccount', '2024-01-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())]"
+ },
+ "primaryAccessKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary access key of the storage account."
+ },
+ "value": "[listKeys('storageAccount', '2024-01-01').keys[0].value]"
+ },
+ "secondayAccessKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary access key of the storage account."
+ },
+ "value": "[listKeys('storageAccount', '2024-01-01').keys[1].value]"
+ },
+ "primaryConnectionString": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary connection string of the storage account."
+ },
+ "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys('storageAccount', '2024-01-01').keys[0].value, environment().suffixes.storage)]"
+ },
+ "secondaryConnectionString": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondary connection string of the storage account."
+ },
+ "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys('storageAccount', '2024-01-01').keys[1].value, environment().suffixes.storage)]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').blob)]",
+ "userAssignedIdentity",
+ "virtualNetwork"
+ ]
+ },
+ "searchServiceUpdate": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.search.update.{0}', variables('solutionSuffix')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('searchServiceName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "disableLocalAuth": {
+ "value": true
+ },
+ "hostingMode": {
+ "value": "default"
+ },
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": true
+ }
+ },
+ "publicNetworkAccess": {
+ "value": "Enabled"
+ },
+ "networkRuleSet": {
+ "value": {
+ "bypass": "AzureServices"
+ }
+ },
+ "partitionCount": {
+ "value": 1
+ },
+ "replicaCount": {
+ "value": 1
+ },
+ "sku": "[if(parameters('enableScalability'), createObject('value', 'standard'), createObject('value', 'basic'))]",
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "roleDefinitionIdOrName": "Search Index Data Contributor",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "principalId": "[variables('deployingUserPrincipalId')]",
+ "roleDefinitionIdOrName": "Search Index Data Contributor",
+ "principalType": "[variables('deployerPrincipalType')]"
+ },
+ {
+ "principalId": "[if(variables('useExistingAiFoundryAiProject'), reference('existingAiFoundryAiServicesProject', '2025-06-01', 'full').identity.principalId, reference('aiFoundryAiServicesProject').outputs.principalId.value)]",
+ "roleDefinitionIdOrName": "Search Index Data Reader",
+ "principalType": "ServicePrincipal"
+ },
+ {
+ "principalId": "[if(variables('useExistingAiFoundryAiProject'), reference('existingAiFoundryAiServicesProject', '2025-06-01', 'full').identity.principalId, reference('aiFoundryAiServicesProject').outputs.principalId.value)]",
+ "roleDefinitionIdOrName": "Search Service Contributor",
+ "principalType": "ServicePrincipal"
+ }
+ ]
+ },
+ "privateEndpoints": {
+ "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.37.4.10188",
+ "templateHash": "10902281417196168235"
+ },
+ "name": "Search Services",
+ "description": "This module deploys a Search 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
+ }
+ },
+ "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.lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "notes": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the notes of the lock."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a lock.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1"
+ }
+ }
+ },
+ "_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.6.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.6.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.6.1"
+ }
+ }
+ },
+ "_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.6.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."
+ }
+ },
+ "notes": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the notes of the lock."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a lock.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "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/_1.lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/_1.roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-07-01#properties/tags"
+ },
+ "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.6.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",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Search/searchServices@2025-02-01-preview#properties/tags"
+ },
+ "description": "Optional. Tags to help categorize the resource in the Azure portal."
+ },
+ "nullable": 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)))))]"
+ }
+ ],
+ "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.11.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ "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": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]"
+ },
+ "dependsOn": [
+ "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())]",
+ "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.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"
+ },
+ "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": [
+ "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.37.4.10188",
+ "templateHash": "557730297583881254"
+ },
+ "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": "2025-02-01-preview",
+ "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('searchService', '2025-02-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), 'value', listAdminKeys('searchService', '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.37.4.10188",
+ "templateHash": "7634110751636246703"
+ }
+ },
+ "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."
+ }
+ }
+ }
+ }
+ },
+ "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": "2024-11-01",
+ "name": "[parameters('keyVaultName')]"
+ },
+ "secrets": {
+ "copy": {
+ "name": "secrets",
+ "count": "[length(parameters('secretsToSet'))]"
+ },
+ "type": "Microsoft.KeyVault/vaults/secrets",
+ "apiVersion": "2024-11-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]"
+ }
+ }
+ }
+ }
+ }
+ },
+ "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', '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",
+ "metadata": {
+ "description": "The endpoint of the search service."
+ },
+ "value": "[reference('searchService').endpoint]"
+ },
+ "privateEndpoints": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/privateEndpointOutputType"
+ },
+ "metadata": {
+ "description": "The private endpoints of the search service."
+ },
+ "copy": {
+ "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]",
+ "input": {
+ "name": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]",
+ "resourceId": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]",
+ "groupId": "[tryGet(tryGet(reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]",
+ "customDnsConfigs": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]",
+ "networkInterfaceResourceIds": "[reference(format('searchService_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())]"
+ },
+ "primaryKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The primary admin API key of the search service."
+ },
+ "value": "[listAdminKeys('searchService', '2025-02-01-preview').primaryKey]"
+ },
+ "secondaryKey": {
+ "type": "securestring",
+ "metadata": {
+ "description": "The secondaryKey admin API key of the search service."
+ },
+ "value": "[listAdminKeys('searchService', '2025-02-01-preview').secondaryKey]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "aiFoundryAiServicesProject",
+ "existingAiFoundryAiServicesProject",
+ "searchService",
+ "userAssignedIdentity"
+ ]
+ },
+ "aiSearchFoundryConnection": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('aifp-srch-connection.{0}', variables('solutionSuffix')), 64)]",
+ "subscriptionId": "[variables('aiFoundryAiServicesSubscriptionId')]",
+ "resourceGroup": "[variables('aiFoundryAiServicesResourceGroupName')]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "aiFoundryProjectName": "[if(variables('useExistingAiFoundryAiProject'), createObject('value', variables('aiFoundryAiProjectResourceName')), createObject('value', reference('aiFoundryAiServicesProject').outputs.name.value))]",
+ "aiFoundryName": {
+ "value": "[variables('aiFoundryAiServicesResourceName')]"
+ },
+ "aifSearchConnectionName": {
+ "value": "[variables('aiSearchConnectionName')]"
+ },
+ "searchServiceResourceId": {
+ "value": "[resourceId('Microsoft.Search/searchServices', variables('searchServiceName'))]"
+ },
+ "searchServiceLocation": {
+ "value": "[reference('searchService', '2024-06-01-preview', 'full').location]"
+ },
+ "searchServiceName": {
+ "value": "[variables('searchServiceName')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.41.2.15936",
+ "templateHash": "8488390916703184584"
+ }
+ },
+ "parameters": {
+ "aifSearchConnectionName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the AI Foundry search connection"
+ }
+ },
+ "searchServiceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the Azure AI Search service"
+ }
+ },
+ "searchServiceResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Resource ID of the Azure AI Search service"
+ }
+ },
+ "searchServiceLocation": {
+ "type": "string",
+ "metadata": {
+ "description": "Location/region of the Azure AI Search service"
+ }
+ },
+ "aiFoundryName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the AI Foundry account"
+ }
+ },
+ "aiFoundryProjectName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the AI Foundry project"
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.CognitiveServices/accounts/projects/connections",
+ "apiVersion": "2025-04-01-preview",
+ "name": "[format('{0}/{1}/{2}', parameters('aiFoundryName'), parameters('aiFoundryProjectName'), parameters('aifSearchConnectionName'))]",
+ "properties": {
+ "category": "CognitiveSearch",
+ "target": "[format('https://{0}.search.windows.net', parameters('searchServiceName'))]",
+ "authType": "AAD",
+ "isSharedToAll": true,
+ "metadata": {
+ "ApiType": "Azure",
+ "ResourceId": "[parameters('searchServiceResourceId')]",
+ "location": "[parameters('searchServiceLocation')]"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "dependsOn": [
+ "aiFoundryAiServices",
+ "aiFoundryAiServicesProject",
+ "searchService"
+ ]
+ },
+ "keyvault": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.key-vault.vault.{0}', variables('keyVaultName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('keyVaultName')]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "sku": "[if(parameters('enableScalability'), createObject('value', 'premium'), createObject('value', 'standard'))]",
+ "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]",
+ "networkAcls": {
+ "value": {
+ "defaultAction": "Allow"
+ }
+ },
+ "enableVaultForDeployment": {
+ "value": true
+ },
+ "enableVaultForDiskEncryption": {
+ "value": true
+ },
+ "enableVaultForTemplateDeployment": {
+ "value": true
+ },
+ "enableRbacAuthorization": {
+ "value": true
+ },
+ "enableSoftDelete": {
+ "value": true
+ },
+ "softDeleteRetentionInDays": {
+ "value": 7
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', createArray()))]",
+ "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-{0}', variables('keyVaultName')), 'customNetworkInterfaceName', format('nic-{0}', variables('keyVaultName')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').keyVault)).outputs.resourceId.value))), 'service', 'vault', 'subnetResourceId', reference('virtualNetwork').outputs.backendSubnetResourceId.value))), createObject('value', createArray()))]",
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "Key Vault Administrator"
+ }
+ ]
+ },
+ "secrets": {
+ "value": []
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ }
+ },
+ "template": {
+ "$schema": "https://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"
+ },
+ "name": "Key Vaults",
+ "description": "This module deploys a Key Vault."
+ },
+ "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
+ }
+ },
+ "credentialOutputType": {
+ "type": "object",
+ "properties": {
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a credential output."
+ }
+ },
+ "accessPolicyType": {
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for an access policy."
+ }
+ },
+ "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/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type for a secret output."
+ }
+ },
+ "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."
+ }
+ },
+ "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."
+ }
+ }
+ },
+ "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": "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."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "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": "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."
+ }
+ },
+ "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."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type for a rotation policy."
+ }
+ },
+ "_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"
+ }
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "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"
+ },
+ "name": "Key Vault Access Policies",
+ "description": "This module deploys a Key Vault Access Policy."
+ },
+ "definitions": {
+ "accessPoliciesType": {
+ "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."
+ }
+ }
+ },
+ "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": {
+ "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": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the secret."
+ }
+ },
+ "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."
+ }
+ },
+ "contentType": {
+ "type": "securestring",
+ "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"
+ },
+ "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')]",
+ "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')]",
+ "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')]"
+ },
+ "secret": {
+ "type": "Microsoft.KeyVault/vaults/secrets",
+ "apiVersion": "2022-07-01",
+ "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "contentType": "[parameters('contentType')]",
+ "attributes": {
+ "enabled": "[parameters('attributesEnabled')]",
+ "exp": "[parameters('attributesExp')]",
+ "nbf": "[parameters('attributesNbf')]"
+ },
+ "value": "[parameters('value')]"
+ }
+ },
+ "secret_roleAssignments": {
+ "copy": {
+ "name": "secret_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))]",
+ "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": [
+ "secret"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the secret."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the secret."
+ },
+ "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 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"
+ },
+ "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": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the key."
+ }
+ },
+ "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": "Optional. The type of the key."
+ }
+ },
+ "releasePolicy": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Key release policy."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "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()))]"
+ },
+ "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]"
+ },
+ "keyUriWithVersion": {
+ "type": "string",
+ "metadata": {
+ "description": "The uri with version of the key."
+ },
+ "value": "[reference('key').keyUriWithVersion]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the key."
+ },
+ "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."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "keyVault"
+ ]
+ },
+ "keyVault_privateEndpoints": {
+ "copy": {
+ "name": "keyVault_privateEndpoints",
+ "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
+ },
+ "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": {
+ "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": "[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.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": {
+ "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.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."
+ },
+ "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": [
+ "keyVault"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the key vault."
+ },
+ "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('name'))]"
+ },
+ "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": {
+ "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"
+ },
+ "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": [
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').keyVault)]",
+ "logAnalyticsWorkspace",
+ "userAssignedIdentity",
+ "virtualNetwork"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the resources were deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "webSiteDefaultHostname": {
+ "type": "string",
+ "metadata": {
+ "description": "The default url of the website to connect to the Multi-Agent Custom Automation Engine solution."
+ },
+ "value": "[reference('webSite').outputs.defaultHostname.value]"
+ },
+ "AZURE_STORAGE_BLOB_URL": {
+ "type": "string",
+ "value": "[reference('avmStorageAccount').outputs.serviceEndpoints.value.blob]"
+ },
+ "AZURE_STORAGE_ACCOUNT_NAME": {
+ "type": "string",
+ "value": "[variables('storageAccountName')]"
+ },
+ "AZURE_AI_SEARCH_ENDPOINT": {
+ "type": "string",
+ "value": "[reference('searchServiceUpdate').outputs.endpoint.value]"
+ },
+ "AZURE_AI_SEARCH_NAME": {
+ "type": "string",
+ "value": "[variables('searchServiceName')]"
+ },
+ "COSMOSDB_ENDPOINT": {
+ "type": "string",
+ "value": "[format('https://{0}.documents.azure.com:443/', variables('cosmosDbResourceName'))]"
+ },
+ "COSMOSDB_DATABASE": {
+ "type": "string",
+ "value": "[variables('cosmosDbDatabaseName')]"
+ },
+ "COSMOSDB_CONTAINER": {
+ "type": "string",
+ "value": "[variables('cosmosDbDatabaseMemoryContainerName')]"
+ },
+ "AZURE_OPENAI_ENDPOINT": {
+ "type": "string",
+ "value": "[format('https://{0}.openai.azure.com/', variables('aiFoundryAiServicesResourceName'))]"
+ },
+ "AZURE_OPENAI_MODEL_NAME": {
+ "type": "string",
+ "value": "[variables('aiFoundryAiServicesModelDeployment').name]"
+ },
+ "AZURE_OPENAI_DEPLOYMENT_NAME": {
+ "type": "string",
+ "value": "[variables('aiFoundryAiServicesModelDeployment').name]"
+ },
+ "AZURE_OPENAI_RAI_DEPLOYMENT_NAME": {
+ "type": "string",
+ "value": "[variables('aiFoundryAiServices4_1ModelDeployment').name]"
+ },
+ "AZURE_OPENAI_API_VERSION": {
+ "type": "string",
+ "value": "[parameters('azureopenaiVersion')]"
+ },
+ "AZURE_AI_SUBSCRIPTION_ID": {
+ "type": "string",
+ "value": "[subscription().subscriptionId]"
+ },
+ "AZURE_AI_RESOURCE_GROUP": {
+ "type": "string",
+ "value": "[resourceGroup().name]"
+ },
+ "AZURE_AI_PROJECT_NAME": {
+ "type": "string",
+ "value": "[if(variables('useExistingAiFoundryAiProject'), variables('aiFoundryAiProjectResourceName'), reference('aiFoundryAiServicesProject').outputs.name.value)]"
+ },
+ "AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME": {
+ "type": "string",
+ "value": "[variables('aiFoundryAiServicesModelDeployment').name]"
+ },
+ "APP_ENV": {
+ "type": "string",
+ "value": "Prod"
+ },
+ "AI_FOUNDRY_RESOURCE_ID": {
+ "type": "string",
+ "value": "[if(not(variables('useExistingAiFoundryAiProject')), reference('aiFoundryAiServices').outputs.resourceId.value, parameters('existingAiFoundryAiProjectResourceId'))]"
+ },
+ "COSMOSDB_ACCOUNT_NAME": {
+ "type": "string",
+ "value": "[variables('cosmosDbResourceName')]"
+ },
+ "AZURE_SEARCH_ENDPOINT": {
+ "type": "string",
+ "value": "[reference('searchServiceUpdate').outputs.endpoint.value]"
+ },
+ "AZURE_CLIENT_ID": {
+ "type": "string",
+ "value": "[reference('userAssignedIdentity').outputs.clientId.value]"
+ },
+ "AZURE_TENANT_ID": {
+ "type": "string",
+ "value": "[tenant().tenantId]"
+ },
+ "AZURE_AI_SEARCH_CONNECTION_NAME": {
+ "type": "string",
+ "value": "[variables('aiSearchConnectionName')]"
+ },
+ "AZURE_COGNITIVE_SERVICES": {
+ "type": "string",
+ "value": "https://cognitiveservices.azure.com/.default"
+ },
+ "REASONING_MODEL_NAME": {
+ "type": "string",
+ "value": "[variables('aiFoundryAiServicesReasoningModelDeployment').name]"
+ },
+ "MCP_SERVER_NAME": {
+ "type": "string",
+ "value": "MacaeMcpServer"
+ },
+ "MCP_SERVER_DESCRIPTION": {
+ "type": "string",
+ "value": "MCP server with greeting, HR, and planning tools"
+ },
+ "SUPPORTED_MODELS": {
+ "type": "string",
+ "value": "[[\"o3\",\"o4-mini\",\"gpt-4.1\",\"gpt-4.1-mini\"]"
+ },
+ "BACKEND_URL": {
+ "type": "string",
+ "value": "[format('https://{0}', reference('containerApp').outputs.fqdn.value)]"
+ },
+ "AZURE_AI_PROJECT_ENDPOINT": {
+ "type": "string",
+ "value": "[if(variables('useExistingAiFoundryAiProject'), reference('existingAiFoundryAiServicesProject').endpoints['AI Foundry API'], reference('aiFoundryAiServicesProject').outputs.apiEndpoint.value)]"
+ },
+ "AZURE_AI_AGENT_ENDPOINT": {
+ "type": "string",
+ "value": "[if(variables('useExistingAiFoundryAiProject'), reference('existingAiFoundryAiServicesProject').endpoints['AI Foundry API'], reference('aiFoundryAiServicesProject').outputs.apiEndpoint.value)]"
+ },
+ "AZURE_AI_AGENT_API_VERSION": {
+ "type": "string",
+ "value": "[parameters('azureAiAgentAPIVersion')]"
+ },
+ "AZURE_AI_AGENT_PROJECT_CONNECTION_STRING": {
+ "type": "string",
+ "value": "[format('{0}.services.ai.azure.com;{1};{2};{3}', variables('aiFoundryAiServicesResourceName'), variables('aiFoundryAiServicesSubscriptionId'), variables('aiFoundryAiServicesResourceGroupName'), variables('aiFoundryAiProjectResourceName'))]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameRetailCustomer')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_RETAIL_ORDER": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameRetailOrder')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_RFP_SUMMARY": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameRFPSummary')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_RFP_RISK": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameRFPRisk')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameRFPCompliance')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameContractSummary')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_CONTRACT_RISK": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameContractRisk')]"
+ },
+ "AZURE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE": {
+ "type": "string",
+ "value": "[parameters('storageContainerNameContractCompliance')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForRetailCustomer')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForRetailOrder')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForRFPSummary')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_RFP_RISK": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForRFPRisk')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForRFPCompliance')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForContractSummary')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForContractRisk')]"
+ },
+ "AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE": {
+ "type": "string",
+ "value": "[variables('aiSearchIndexNameForContractCompliance')]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/infra/main.parameters.json b/infra/main.parameters.json
index 3bf39983c..ed6ba532a 100644
--- a/infra/main.parameters.json
+++ b/infra/main.parameters.json
@@ -48,22 +48,31 @@
"value": "${AZURE_ENV_REASONING_MODEL_CAPACITY}"
},
"backendContainerImageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG=latest_v3}"
+ "value": "${AZURE_ENV_IMAGE_TAG}"
},
"frontendContainerImageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG=latest_v3}"
+ "value": "${AZURE_ENV_IMAGE_TAG}"
},
"MCPContainerImageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG=latest_v3}"
+ "value": "${AZURE_ENV_IMAGE_TAG}"
},
"enableTelemetry": {
"value": "${AZURE_ENV_ENABLE_TELEMETRY}"
},
"existingLogAnalyticsWorkspaceId": {
- "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
+ "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
},
"existingAiFoundryAiProjectResourceId": {
"value": "${AZURE_EXISTING_AI_PROJECT_RESOURCE_ID}"
+ },
+ "backendContainerRegistryHostname": {
+ "value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
+ },
+ "frontendContainerRegistryHostname": {
+ "value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
+ },
+ "MCPContainerRegistryHostname": {
+ "value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
}
}
-}
+}
\ No newline at end of file
diff --git a/infra/main.waf.parameters.json b/infra/main.waf.parameters.json
index b6bf190a3..19607121f 100644
--- a/infra/main.waf.parameters.json
+++ b/infra/main.waf.parameters.json
@@ -48,13 +48,13 @@
"value": "${AZURE_ENV_REASONING_MODEL_CAPACITY}"
},
"backendContainerImageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG=latest_v3}"
+ "value": "${AZURE_ENV_IMAGE_TAG=latest_v4}"
},
"frontendContainerImageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG=latest_v3}"
+ "value": "${AZURE_ENV_IMAGE_TAG=latest_v4}"
},
"MCPContainerImageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG=latest_v3}"
+ "value": "${AZURE_ENV_IMAGE_TAG=latest_v4}"
},
"enableTelemetry": {
"value": "${AZURE_ENV_ENABLE_TELEMETRY}"
@@ -74,30 +74,23 @@
"virtualMachineAdminPassword": {
"value": "${AZURE_ENV_VM_ADMIN_PASSWORD}"
},
+ "virtualMachineSize": {
+ "value": "${AZURE_ENV_VM_SIZE}"
+ },
"existingLogAnalyticsWorkspaceId": {
- "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
+ "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
},
"existingAiFoundryAiProjectResourceId": {
"value": "${AZURE_EXISTING_AI_PROJECT_RESOURCE_ID}"
},
- "allowedFqdnList": {
- "value": [
- "mcr.microsoft.com",
- "openai.azure.com",
- "cognitiveservices.azure.com",
- "login.microsoftonline.com",
- "management.azure.com",
- "aiinfra.azure.com",
- "aiinfra.azure.net",
- "aiinfra.azureedge.net",
- "blob.core.windows.net",
- "database.windows.net",
- "vault.azure.net",
- "monitoring.azure.com",
- "dc.services.visualstudio.com",
- "azconfig.io",
- "azconfig.azure.net"
- ]
+ "backendContainerRegistryHostname": {
+ "value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
+ },
+ "frontendContainerRegistryHostname": {
+ "value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
+ },
+ "MCPContainerRegistryHostname": {
+ "value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
}
}
}
\ No newline at end of file
diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep
index 77ee8afb5..b7961b9b3 100644
--- a/infra/main_custom.bicep
+++ b/infra/main_custom.bicep
@@ -56,10 +56,6 @@ param gptModelName string = 'gpt-4.1-mini'
@description('Optional. Version of the GPT model to deploy. Defaults to 2025-04-14.')
param gptModelVersion string = '2025-04-14'
-@minLength(1)
-@description('Optional. Name of the GPT Reasoning model to deploy:')
-param gptReasoningModelName string = 'o4-mini'
-
@minLength(1)
@description('Optional. Name of the GPT model to deploy:')
param gpt4_1ModelName string = 'gpt-4.1'
@@ -67,12 +63,18 @@ param gpt4_1ModelName string = 'gpt-4.1'
@description('Optional. Version of the GPT model to deploy. Defaults to 2025-04-14.')
param gpt4_1ModelVersion string = '2025-04-14'
-@description('Optional. Version of the GPT Reasoning model to deploy. Defaults to 2025-04-14.')
+@minLength(1)
+@description('Optional. Name of the GPT Reasoning model to deploy:')
+param gptReasoningModelName string = 'o4-mini'
+
+@description('Optional. Version of the GPT Reasoning model to deploy. Defaults to 2025-04-16.')
param gptReasoningModelVersion string = '2025-04-16'
-@description('Optional. Version of the Azure OpenAI service to deploy. Defaults to 2025-01-01-preview.')
+@description('Optional. Version of the Azure OpenAI service to deploy. Defaults to 2024-12-01-preview.')
param azureopenaiVersion string = '2024-12-01-preview'
+@description('Optional. Version of the Azure AI Agent API version. Defaults to 2025-01-01-preview.')
+param azureAiAgentAPIVersion string = '2025-01-01-preview'
@minLength(1)
@allowed([
@@ -124,12 +126,14 @@ param enablePrivateNetworking bool = false
@secure()
@description('Optional. The user name for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true.')
-param virtualMachineAdminUsername string = take(newGuid(), 20)
+param virtualMachineAdminUsername string?
@description('Optional. The password for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true.')
@secure()
-param virtualMachineAdminPassword string = newGuid()
+param virtualMachineAdminPassword string?
+@description('Optional. The size of the virtual machine. Defaults to Standard_D2s_v5.')
+param virtualMachineSize string = 'Standard_D2s_v5'
// These parameters are changed for testing - please reset as part of publication
@description('Optional. The Container Registry hostname where the docker images for the backend are located.')
@@ -139,7 +143,7 @@ param backendContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
param backendContainerImageName string = 'macaebackend'
@description('Optional. The Container Image Tag to deploy on the backend.')
-param backendContainerImageTag string = 'latest_v3'
+param backendContainerImageTag string = 'latest_v4'
@description('Optional. The Container Registry hostname where the docker images for the frontend are located.')
param frontendContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
@@ -148,7 +152,7 @@ param frontendContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
param frontendContainerImageName string = 'macaefrontend'
@description('Optional. The Container Image Tag to deploy on the frontend.')
-param frontendContainerImageTag string = 'latest_v3'
+param frontendContainerImageTag string = 'latest_v4'
@description('Optional. The Container Registry hostname where the docker images for the MCP are located.')
param MCPContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
@@ -157,7 +161,7 @@ param MCPContainerRegistryHostname string = 'biabcontainerreg.azurecr.io'
param MCPContainerImageName string = 'macaemcp'
@description('Optional. The Container Image Tag to deploy on the MCP.')
-param MCPContainerImageTag string = 'latest_v3'
+param MCPContainerImageTag string = 'latest_v4'
@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true
@@ -223,19 +227,27 @@ var allTags = union(
},
tags
)
+var existingTags = resourceGroup().tags ?? {}
@description('Tag, Created by user name')
-param createdBy string = contains(deployer(), 'userPrincipalName')? split(deployer().userPrincipalName, '@')[0]: deployer().objectId
+param createdBy string = contains(deployer(), 'userPrincipalName')
+ ? split(deployer().userPrincipalName, '@')[0]
+ : deployer().objectId
+var deployerPrincipalType = contains(deployer(), 'userPrincipalName') ? 'User' : 'ServicePrincipal'
resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = {
name: 'default'
properties: {
- tags: {
- ...allTags
- TemplateName: 'MACAE'
- Type: enablePrivateNetworking ? 'WAF' : 'Non-WAF'
- CreatedBy: createdBy
- DeploymentName: deployment().name
- }
+ tags: union(
+ existingTags,
+ allTags,
+ {
+ TemplateName: 'MACAE'
+ Type: enablePrivateNetworking ? 'WAF' : 'Non-WAF'
+ CreatedBy: createdBy
+ DeploymentName: deployment().name
+ SolutionSuffix: solutionSuffix
+ }
+ )
}
}
@@ -363,7 +375,6 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en
flowType: 'Bluefield'
// WAF aligned configuration for Monitoring
workspaceResourceId: enableMonitoring ? logAnalyticsWorkspaceResourceId : ''
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
}
}
@@ -379,338 +390,20 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id
enableTelemetry: enableTelemetry
}
}
-
-// ========== Network Security Groups ========== //
-// WAF best practices for virtual networks: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/virtual-network
-// WAF recommendations for networking and connectivity: https://learn.microsoft.com/en-us/azure/well-architected/security/networking
-var networkSecurityGroupBackendResourceName = 'nsg-${solutionSuffix}-backend'
-module networkSecurityGroupBackend 'br/public:avm/res/network/network-security-group:0.5.1' = if (enablePrivateNetworking) {
- name: take('avm.res.network.network-security-group.backend.${networkSecurityGroupBackendResourceName}', 64)
- params: {
- name: networkSecurityGroupBackendResourceName
- location: location
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
- securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
- }
- }
- ]
- }
-}
-
-var networkSecurityGroupBastionResourceName = 'nsg-${solutionSuffix}-bastion'
-module networkSecurityGroupBastion 'br/public:avm/res/network/network-security-group:0.5.1' = if (enablePrivateNetworking) {
- name: take('avm.res.network.network-security-group.bastion${networkSecurityGroupBastionResourceName}', 64)
- params: {
- name: networkSecurityGroupBastionResourceName
- location: location
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
- securityRules: [
- {
- name: 'AllowHttpsInBound'
- properties: {
- protocol: 'Tcp'
- sourcePortRange: '*'
- sourceAddressPrefix: 'Internet'
- 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'
- }
- }
- ]
- }
-}
-
-var networkSecurityGroupAdministrationResourceName = 'nsg-${solutionSuffix}-administration'
-module networkSecurityGroupAdministration 'br/public:avm/res/network/network-security-group:0.5.1' = if (enablePrivateNetworking) {
- name: take('avm.res.network.network-security-group.administration.${networkSecurityGroupAdministrationResourceName}', 64)
- params: {
- name: networkSecurityGroupAdministrationResourceName
- location: location
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
- securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
- }
- }
- ]
- }
-}
-
-var networkSecurityGroupContainersResourceName = 'nsg-${solutionSuffix}-containers'
-module networkSecurityGroupContainers 'br/public:avm/res/network/network-security-group:0.5.1' = if (enablePrivateNetworking) {
- name: take('avm.res.network.network-security-group.containers.${networkSecurityGroupContainersResourceName}', 64)
- params: {
- name: networkSecurityGroupContainersResourceName
- location: location
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
- securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
- }
- }
- ]
- }
-}
-
-var networkSecurityGroupWebsiteResourceName = 'nsg-${solutionSuffix}-website'
-module networkSecurityGroupWebsite 'br/public:avm/res/network/network-security-group:0.5.1' = if (enablePrivateNetworking) {
- name: take('avm.res.network.network-security-group.website.${networkSecurityGroupWebsiteResourceName}', 64)
- params: {
- name: networkSecurityGroupWebsiteResourceName
- location: location
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
- securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
- }
- }
- ]
- }
-}
-
// ========== Virtual Network ========== //
// WAF best practices for virtual networks: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/virtual-network
// WAF recommendations for networking and connectivity: https://learn.microsoft.com/en-us/azure/well-architected/security/networking
var virtualNetworkResourceName = 'vnet-${solutionSuffix}'
-module virtualNetwork 'br/public:avm/res/network/virtual-network:0.7.0' = if (enablePrivateNetworking) {
- name: take('avm.res.network.virtual-network.${virtualNetworkResourceName}', 64)
+module virtualNetwork 'modules/virtualNetwork.bicep' = if (enablePrivateNetworking) {
+ name: take('module.virtualNetwork.${solutionSuffix}', 64)
params: {
- name: virtualNetworkResourceName
+ name: 'vnet-${solutionSuffix}'
location: location
tags: tags
enableTelemetry: enableTelemetry
addressPrefixes: ['10.0.0.0/8']
- subnets: [
- {
- name: 'backend'
- addressPrefix: '10.0.0.0/27'
- networkSecurityGroupResourceId: networkSecurityGroupBackend!.outputs.resourceId
- }
- {
- name: 'administration'
- addressPrefix: '10.0.0.32/27'
- networkSecurityGroupResourceId: networkSecurityGroupAdministration!.outputs.resourceId
- //natGatewayResourceId: natGateway.outputs.resourceId
- }
- {
- // For Azure Bastion resources deployed on or after November 2, 2021, the minimum AzureBastionSubnet size is /26 or larger (/25, /24, etc.).
- // https://learn.microsoft.com/en-us/azure/bastion/configuration-settings#subnet
- name: 'AzureBastionSubnet' //This exact name is required for Azure Bastion
- addressPrefix: '10.0.0.64/26'
- networkSecurityGroupResourceId: networkSecurityGroupBastion!.outputs.resourceId
- }
- {
- // If you use your own vnw, you need to provide a subnet that is dedicated exclusively to the Container App environment you deploy. This subnet isn't available to other services
- // https://learn.microsoft.com/en-us/azure/container-apps/networking?tabs=workload-profiles-env%2Cazure-cli#custom-vnw-configuration
- name: 'containers'
- addressPrefix: '10.0.2.0/23' //subnet of size /23 is required for container app
- delegation: 'Microsoft.App/environments'
- networkSecurityGroupResourceId: networkSecurityGroupContainers!.outputs.resourceId
- privateEndpointNetworkPolicies: 'Enabled'
- privateLinkServiceNetworkPolicies: 'Enabled'
- }
- {
- // If you use your own vnw, you need to provide a subnet that is dedicated exclusively to the App Environment you deploy. This subnet isn't available to other services
- // https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration#subnet-requirements
- name: 'webserverfarm'
- addressPrefix: '10.0.4.0/27' //When you're creating subnets in Azure portal as part of integrating with the virtual network, a minimum size of /27 is required
- delegation: 'Microsoft.Web/serverfarms'
- networkSecurityGroupResourceId: networkSecurityGroupWebsite!.outputs.resourceId
- privateEndpointNetworkPolicies: 'Enabled'
- privateLinkServiceNetworkPolicies: 'Enabled'
- }
- ]
+ logAnalyticsWorkspaceId: logAnalyticsWorkspaceResourceId
+ resourceSuffix: solutionSuffix
}
}
@@ -727,7 +420,7 @@ module bastionHost 'br/public:avm/res/network/bastion-host:0.7.0' = if (enablePr
enableTelemetry: enableTelemetry
tags: tags
virtualNetworkResourceId: virtualNetwork!.?outputs.?resourceId
- availabilityZones:[]
+ availabilityZones: []
publicIPAddressObject: {
name: 'pip-bas${solutionSuffix}'
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
@@ -912,7 +605,6 @@ module proximityPlacementGroup 'br/public:avm/res/compute/proximity-placement-gr
var virtualMachineResourceName = 'vm-${solutionSuffix}'
var virtualMachineAvailabilityZone = 1
-var virtualMachineSize = 'Standard_D2s_v3'
module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (enablePrivateNetworking) {
name: take('avm.res.compute.virtual-machine.${virtualMachineResourceName}', 64)
params: {
@@ -923,8 +615,8 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (e
computerName: take(virtualMachineResourceName, 15)
osType: 'Windows'
vmSize: virtualMachineSize
- adminUsername: virtualMachineAdminUsername
- adminPassword: virtualMachineAdminPassword
+ adminUsername: virtualMachineAdminUsername ?? 'JumpboxAdminUser'
+ adminPassword: virtualMachineAdminPassword ?? 'JumpboxAdminP@ssw0rd1234!'
patchMode: 'AutomaticByPlatform'
bypassPlatformSafetyChecksOnUserSchedule: true
maintenanceConfigurationResourceId: maintenanceConfiguration!.outputs.resourceId
@@ -959,7 +651,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (e
ipConfigurations: [
{
name: '${virtualMachineResourceName}-nic01-ipconfig01'
- subnetResourceId: virtualNetwork!.outputs.subnetResourceIds[1]
+ subnetResourceId: virtualNetwork!.outputs.administrationSubnetResourceId
diagnosticSettings: enableMonitoring //WAF aligned configuration for Monitoring
? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }]
: null
@@ -1262,42 +954,55 @@ module aiFoundryAiServices 'br:mcr.microsoft.com/bicep/avm/res/cognitive-service
{
roleDefinitionIdOrName: '53ca6127-db72-4b80-b1b0-d745d6d5456d' // Azure AI User
principalId: deployingUserPrincipalId
- principalType: 'User'
+ principalType: deployerPrincipalType
}
{
roleDefinitionIdOrName: '64702f94-c441-49e6-a78b-ef80e0188fee' // Azure AI Developer
principalId: deployingUserPrincipalId
- principalType: 'User'
+ principalType: deployerPrincipalType
}
]
// WAF aligned configuration for Monitoring
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
- privateEndpoints: (enablePrivateNetworking)
- ? ([
- {
- name: 'pep-${aiFoundryAiServicesResourceName}'
- customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}'
- subnetResourceId: virtualNetwork!.outputs.subnetResourceIds[0]
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [
- {
- name: 'ai-services-dns-zone-cognitiveservices'
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId
- }
- {
- name: 'ai-services-dns-zone-openai'
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId
- }
- {
- name: 'ai-services-dns-zone-aiservices'
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId
- }
- ]
- }
- }
- ])
- : []
+ // Private endpoints are deployed separately via the aiFoundryPrivateEndpoint module below
+ privateEndpoints: []
+ }
+}
+
+module aiFoundryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.8.1' = if (enablePrivateNetworking && !useExistingAiFoundryAiProject) {
+ name: take('pep-${aiFoundryAiServicesResourceName}-deployment', 64)
+ params: {
+ name: 'pep-${aiFoundryAiServicesResourceName}'
+ customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}'
+ location: location
+ tags: tags
+ privateLinkServiceConnections: [
+ {
+ name: 'pep-${aiFoundryAiServicesResourceName}-connection'
+ properties: {
+ privateLinkServiceId: aiFoundryAiServices!.outputs.resourceId
+ groupIds: ['account']
+ }
+ }
+ ]
+ privateDnsZoneGroup: {
+ privateDnsZoneGroupConfigs: [
+ {
+ name: 'ai-services-dns-zone-cognitiveservices'
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId
+ }
+ {
+ name: 'ai-services-dns-zone-openai'
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId
+ }
+ {
+ name: 'ai-services-dns-zone-aiservices'
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId
+ }
+ ]
+ }
+ subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
}
}
@@ -1308,6 +1013,7 @@ resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/account
module aiFoundryAiServicesProject 'modules/ai-project.bicep' = if (!useExistingAiFoundryAiProject) {
name: take('module.ai-project.${aiFoundryAiProjectResourceName}', 64)
+ dependsOn: enablePrivateNetworking ? [ aiFoundryPrivateEndpoint ] : []
params: {
name: aiFoundryAiProjectResourceName
location: azureAiServiceLocation
@@ -1391,7 +1097,7 @@ module cosmosDb 'br/public:avm/res/document-db/database-account:0.15.0' = {
]
}
service: 'Sql'
- subnetResourceId: virtualNetwork!.outputs.subnetResourceIds[0]
+ subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
}
]
: []
@@ -1436,7 +1142,7 @@ module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.2
// WAF aligned configuration for Private Networking
publicNetworkAccess: 'Enabled' // Always enabling the publicNetworkAccess for Container App Environment
internal: false // Must be false when publicNetworkAccess is'Enabled'
- infrastructureSubnetResourceId: enablePrivateNetworking ? virtualNetwork.?outputs.?subnetResourceIds[3] : null
+ infrastructureSubnetResourceId: enablePrivateNetworking ? virtualNetwork.?outputs.?containerSubnetResourceId : null
// WAF aligned configuration for Monitoring
appLogsConfiguration: enableMonitoring
? {
@@ -1493,7 +1199,10 @@ module containerRegistry 'br/public:avm/res/container-registry/registry:0.9.1' =
}
}
-var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
+var acrPullRole = subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ '7f951dda-4ed3-4680-a7ca-43fe172d538d'
+)
// ========== Backend Container App Service ========== //
// WAF best practices for container apps: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-container-apps
@@ -1516,7 +1225,7 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
'https://${webSiteResourceName}.azurewebsites.net'
'http://${webSiteResourceName}.azurewebsites.net'
]
- allowedMethods:[
+ allowedMethods: [
'GET'
'POST'
'PUT'
@@ -1579,6 +1288,10 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
value: aiFoundryAiServicesModelDeployment.name
}
+ {
+ name: 'AZURE_OPENAI_RAI_DEPLOYMENT_NAME'
+ value: aiFoundryAiServices4_1ModelDeployment.name
+ }
{
name: 'AZURE_OPENAI_API_VERSION'
value: azureopenaiVersion
@@ -1607,10 +1320,10 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'FRONTEND_SITE_NAME'
value: 'https://${webSiteResourceName}.azurewebsites.net'
}
- {
- name: 'AZURE_AI_AGENT_ENDPOINT'
- value: aiFoundryAiProjectEndpoint
- }
+ // {
+ // name: 'AZURE_AI_AGENT_ENDPOINT'
+ // value: aiFoundryAiProjectEndpoint
+ // }
{
name: 'AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME'
value: aiFoundryAiServicesModelDeployment.name
@@ -1623,26 +1336,22 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'AZURE_AI_SEARCH_CONNECTION_NAME'
value: aiSearchConnectionName
}
- {
- name: 'AZURE_AI_SEARCH_INDEX_NAME'
- value: aiSearchIndexName
- }
{
name: 'AZURE_AI_SEARCH_ENDPOINT'
- value: searchService.outputs.endpoint
+ value: searchServiceUpdate.outputs.endpoint
}
{
name: 'AZURE_COGNITIVE_SERVICES'
- value: 'https://cognitiveservices.azure.com/.default'
+ value: 'https://cognitiveservices.azure.com/.default'
}
{
name: 'AZURE_BING_CONNECTION_NAME'
- value: 'binggrnd'
+ value: 'binggrnd'
}
{
name: 'BING_CONNECTION_NAME'
- value: 'binggrnd'
- }
+ value: 'binggrnd'
+ }
{
name: 'REASONING_MODEL_NAME'
value: aiFoundryAiServicesReasoningModelDeployment.name
@@ -1653,11 +1362,11 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
}
{
name: 'MCP_SERVER_NAME'
- value: 'MacaeMcpServer'
+ value: 'MacaeMcpServer'
}
{
name: 'MCP_SERVER_DESCRIPTION'
- value: 'MCP server with greeting, HR, and planning tools'
+ value: 'MCP server with greeting, HR, and planning tools'
}
{
name: 'AZURE_TENANT_ID'
@@ -1669,41 +1378,59 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
}
{
name: 'SUPPORTED_MODELS'
- value: '["o3","o4-mini","gpt-4.1","gpt-4.1-mini"]'
- }
- {
- name: 'AZURE_AI_SEARCH_API_KEY'
- value: 'azure-ai-search-api-key'
+ value: '["o3","o4-mini","gpt-4.1","gpt-4.1-mini"]'
}
{
name: 'AZURE_STORAGE_BLOB_URL'
value: avmStorageAccount.outputs.serviceEndpoints.blob
}
- {
- name: 'AZURE_STORAGE_CONTAINER_NAME'
- value: storageContainerName
- }
{
name: 'AZURE_AI_MODEL_DEPLOYMENT_NAME'
value: aiFoundryAiServicesModelDeployment.name
}
+ {
+ name: 'AZURE_AI_PROJECT_ENDPOINT'
+ value: aiFoundryAiProjectEndpoint
+ }
+ {
+ name: 'AZURE_AI_AGENT_ENDPOINT'
+ value: aiFoundryAiProjectEndpoint
+ }
+ {
+ name: 'AZURE_AI_AGENT_API_VERSION'
+ value: azureAiAgentAPIVersion
+ }
+ {
+ name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING'
+ value: '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
+ }
+ {
+ name: 'AZURE_DEV_COLLECT_TELEMETRY'
+ value: 'no'
+ }
+ {
+ name: 'AZURE_BASIC_LOGGING_LEVEL'
+ value: 'INFO'
+ }
+ {
+ name: 'AZURE_PACKAGE_LOGGING_LEVEL'
+ value: 'WARNING'
+ }
+ {
+ name: 'AZURE_LOGGING_PACKAGES'
+ value: ''
+ }
]
}
]
- secrets: [
- {
- name: 'azure-ai-search-api-key'
- keyVaultUrl: keyvault.outputs.secrets[0].uriWithVersion
- identity: userAssignedIdentity.outputs.resourceId
- }
- ]
+ secrets: []
}
}
// ========== MCP Container App Service ========== //
// WAF best practices for container apps: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-container-apps
// PSRule for Container App: https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/#container-app
-var containerAppMcpResourceName = 'ca-mcp-${solutionSuffix}'
+var containerAppMcpResourceName = 'ca-mcp-${solutionSuffix}'
module containerAppMcp 'br/public:avm/res/app/container-app:0.18.1' = {
name: take('avm.res.app.container-app.${containerAppMcpResourceName}', 64)
params: {
@@ -1819,7 +1546,7 @@ module webServerFarm 'br/public:avm/res/web/serverfarm:0.5.0' = {
// WAF aligned configuration for Monitoring
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
// WAF aligned configuration for Scalability
- skuName: enableScalability || enableRedundancy ? 'P1v3' : 'B3'
+ skuName: enableScalability || enableRedundancy ? 'P1v4' : 'B3'
skuCapacity: enableScalability ? 3 : 1
// WAF aligned configuration for Redundancy
zoneRedundant: enableRedundancy ? true : false
@@ -1840,6 +1567,9 @@ module webSite 'modules/web-sites.bicep' = {
location: location
kind: 'app,linux'
serverFarmResourceId: webServerFarm.?outputs.resourceId
+ managedIdentities: {
+ systemAssigned: true
+ }
siteConfig: {
//linuxFxVersion: 'DOCKER|${frontendContainerRegistryHostname}/${frontendContainerImageName}:${frontendContainerImageTag}'
minTlsVersion: '1.2'
@@ -1866,17 +1596,24 @@ module webSite 'modules/web-sites.bicep' = {
// WAF aligned configuration for Private Networking
vnetRouteAllEnabled: enablePrivateNetworking ? true : false
vnetImagePullEnabled: enablePrivateNetworking ? true : false
- virtualNetworkSubnetId: enablePrivateNetworking ? virtualNetwork!.outputs.subnetResourceIds[4] : null
+ virtualNetworkSubnetId: enablePrivateNetworking ? virtualNetwork!.outputs.webserverfarmSubnetResourceId : null
publicNetworkAccess: 'Enabled' // Always enabling the public network access for Web App
e2eEncryptionEnabled: true
}
}
-
// ========== Storage Account ========== //
var storageAccountName = replace('st${solutionSuffix}', '-', '')
param storageContainerName string = 'sample-dataset'
+param storageContainerNameRetailCustomer string = 'retail-dataset-customer'
+param storageContainerNameRetailOrder string = 'retail-dataset-order'
+param storageContainerNameRFPSummary string = 'rfp-summary-dataset'
+param storageContainerNameRFPRisk string = 'rfp-risk-dataset'
+param storageContainerNameRFPCompliance string = 'rfp-compliance-dataset'
+param storageContainerNameContractSummary string = 'contract-summary-dataset'
+param storageContainerNameContractRisk string = 'contract-risk-dataset'
+param storageContainerNameContractCompliance string = 'contract-compliance-dataset'
module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
name: take('avm.res.storage.storage-account.${storageAccountName}', 64)
params: {
@@ -1898,20 +1635,20 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
{
principalId: deployingUserPrincipalId
roleDefinitionIdOrName: 'Storage Blob Data Contributor'
- principalType: 'User'
+ principalType: deployerPrincipalType
}
]
// WAF aligned networking
networkAcls: {
bypass: 'AzureServices'
- defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow'
+ defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow'
}
allowBlobPublicAccess: false
- publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
+ publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
// Private endpoints for blob
- privateEndpoints: enablePrivateNetworking
+ privateEndpoints: enablePrivateNetworking
? [
{
name: 'pep-blob-${solutionSuffix}'
@@ -1924,7 +1661,7 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
}
]
}
- subnetResourceId: virtualNetwork!.outputs.subnetResourceIds[0]
+ subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
service: 'blob'
}
]
@@ -1935,7 +1672,35 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
containerDeleteRetentionPolicyEnabled: true
containers: [
{
- name: storageContainerName
+ name: storageContainerNameRetailCustomer
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRetailOrder
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRFPSummary
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRFPRisk
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameRFPCompliance
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameContractSummary
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameContractRisk
+ publicAccess: 'None'
+ }
+ {
+ name: storageContainerNameContractCompliance
publicAccess: 'None'
}
]
@@ -1950,16 +1715,30 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = {
var searchServiceName = 'srch-${solutionSuffix}'
var aiSearchIndexName = 'sample-dataset-index'
-module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
- name: take('avm.res.search.search-service.${solutionSuffix}', 64)
+var aiSearchIndexNameForContractSummary = 'contract-summary-doc-index'
+var aiSearchIndexNameForContractRisk = 'contract-risk-doc-index'
+var aiSearchIndexNameForContractCompliance = 'contract-compliance-doc-index'
+var aiSearchIndexNameForRetailCustomer = 'macae-retail-customer-index'
+var aiSearchIndexNameForRetailOrder = 'macae-retail-order-index'
+var aiSearchIndexNameForRFPSummary = 'macae-rfp-summary-index'
+var aiSearchIndexNameForRFPRisk = 'macae-rfp-risk-index'
+var aiSearchIndexNameForRFPCompliance = 'macae-rfp-compliance-index'
+
+resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' = {
+ name: searchServiceName
+ location: location
+ sku: {
+ name: enableScalability ? 'standard' : 'basic'
+ }
+}
+
+// Separate module for Search Service to enable managed identity and update other properties, as this reduces deployment time
+module searchServiceUpdate 'br/public:avm/res/search/search-service:0.11.1' = {
+ name: take('avm.res.search.update.${solutionSuffix}', 64)
params: {
name: searchServiceName
- authOptions: {
- aadOrApiKey: {
- aadAuthFailureMode: 'http401WithBearerChallenge'
- }
- }
- disableLocalAuth: false
+ location: location
+ disableLocalAuth: true
hostingMode: 'default'
managedIdentities: {
systemAssigned: true
@@ -1985,7 +1764,7 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
{
principalId: deployingUserPrincipalId
roleDefinitionIdOrName: 'Search Index Data Contributor'
- principalType: 'User'
+ principalType: deployerPrincipalType
}
{
principalId: aiFoundryAiProjectPrincipalId
@@ -1998,10 +1777,10 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
principalType: 'ServicePrincipal'
}
]
- privateEndpoints:[]
- // Removing the Private endpoints as we are facing the issue with connecting to search service while comminicating with agents
+ //Removing the Private endpoints as we are facing the issue with connecting to search service while comminicating with agents
+ privateEndpoints: []
// privateEndpoints: enablePrivateNetworking
// ? [
// {
@@ -2020,9 +1799,12 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
// ]
// : []
}
+ dependsOn: [
+ searchService
+ ]
}
-// ========== Search Service - AI Project Connection ========== //
+// ========== Search Service - AI Project Connection ==========//
var aiSearchConnectionName = 'aifp-srch-connection-${solutionSuffix}'
module aiSearchFoundryConnection 'modules/aifp-connections.bicep' = {
@@ -2032,17 +1814,15 @@ module aiSearchFoundryConnection 'modules/aifp-connections.bicep' = {
aiFoundryProjectName: aiFoundryAiProjectName
aiFoundryName: aiFoundryAiServicesResourceName
aifSearchConnectionName: aiSearchConnectionName
- searchServiceResourceId: searchService.outputs.resourceId
- searchServiceLocation: searchService.outputs.location
- searchServiceName: searchService.outputs.name
- searchApiKey: searchService.outputs.primaryKey
+ searchServiceResourceId: searchService.id
+ searchServiceLocation: searchService.location
+ searchServiceName: searchService.name
}
dependsOn: [
aiFoundryAiServices
]
}
-
// ========== KeyVault ========== //
var keyVaultName = 'kv-${solutionSuffix}'
module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
@@ -2062,9 +1842,7 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
enableRbacAuthorization: true
enableSoftDelete: true
softDeleteRetentionInDays: 7
- diagnosticSettings: enableMonitoring
- ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }]
- : []
+ diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : []
// WAF aligned configuration for Private Networking
privateEndpoints: enablePrivateNetworking
? [
@@ -2072,27 +1850,24 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
name: 'pep-${keyVaultName}'
customNetworkInterfaceName: 'nic-${keyVaultName}'
privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [{ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.keyVault]!.outputs.resourceId }]
+ privateDnsZoneGroupConfigs: [
+ { privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.keyVault]!.outputs.resourceId }
+ ]
}
service: 'vault'
- subnetResourceId: virtualNetwork!.outputs.subnetResourceIds[0]
+ subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId
}
]
: []
// WAF aligned configuration for Role-based Access Control
roleAssignments: [
{
- principalId: userAssignedIdentity.outputs.principalId
- principalType: 'ServicePrincipal'
- roleDefinitionIdOrName: 'Key Vault Administrator'
- }
- ]
- secrets: [
- {
- name: 'AzureAISearchAPIKey'
- value: searchService.outputs.primaryKey
+ principalId: userAssignedIdentity.outputs.principalId
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Key Vault Administrator'
}
]
+ secrets: []
enableTelemetry: enableTelemetry
}
}
@@ -2107,28 +1882,10 @@ output resourceGroupName string = resourceGroup().name
@description('The default url of the website to connect to the Multi-Agent Custom Automation Engine solution.')
output webSiteDefaultHostname string = webSite.outputs.defaultHostname
-output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer
-
-// @description('The name of the resource.')
-// output name string = .name
-
-// @description('The location the resource was deployed into.')
-// output location string = .location
-
-// ================ //
-// Definitions //
-// ================ //
-//
-// Add your User-defined-types here, if any
-//
-
-
output AZURE_STORAGE_BLOB_URL string = avmStorageAccount.outputs.serviceEndpoints.blob
output AZURE_STORAGE_ACCOUNT_NAME string = storageAccountName
-output AZURE_STORAGE_CONTAINER_NAME string = storageContainerName
-output AZURE_AI_SEARCH_ENDPOINT string = searchService.outputs.endpoint
-output AZURE_AI_SEARCH_NAME string = searchService.outputs.name
-output AZURE_AI_SEARCH_INDEX_NAME string = aiSearchIndexName
+output AZURE_AI_SEARCH_ENDPOINT string = searchServiceUpdate.outputs.endpoint
+output AZURE_AI_SEARCH_NAME string = searchService.name
output COSMOSDB_ENDPOINT string = 'https://${cosmosDbResourceName}.documents.azure.com:443/'
output COSMOSDB_DATABASE string = cosmosDbDatabaseName
@@ -2136,6 +1893,7 @@ output COSMOSDB_CONTAINER string = cosmosDbDatabaseMemoryContainerName
output AZURE_OPENAI_ENDPOINT string = 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
output AZURE_OPENAI_MODEL_NAME string = aiFoundryAiServicesModelDeployment.name
output AZURE_OPENAI_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
+output AZURE_OPENAI_RAI_DEPLOYMENT_NAME string = aiFoundryAiServices4_1ModelDeployment.name
output AZURE_OPENAI_API_VERSION string = azureopenaiVersion
// output APPLICATIONINSIGHTS_INSTRUMENTATION_KEY string = applicationInsights.outputs.instrumentationKey
// output AZURE_AI_PROJECT_ENDPOINT string = aiFoundryAiServices.outputs.aiProjectInfo.apiEndpoint
@@ -2145,18 +1903,47 @@ output AZURE_AI_PROJECT_NAME string = aiFoundryAiProjectName
output AZURE_AI_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
// output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.outputs.connectionString
output AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
-output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
+// output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
output APP_ENV string = 'Prod'
-output AI_FOUNDRY_RESOURCE_ID string = !useExistingAiFoundryAiProject ? aiFoundryAiServices.outputs.resourceId : existingAiFoundryAiProjectResourceId
+output AI_FOUNDRY_RESOURCE_ID string = !useExistingAiFoundryAiProject
+ ? aiFoundryAiServices.outputs.resourceId
+ : existingAiFoundryAiProjectResourceId
output COSMOSDB_ACCOUNT_NAME string = cosmosDbResourceName
-output AZURE_SEARCH_ENDPOINT string =searchService.outputs.endpoint
-output AZURE_CLIENT_ID string = userAssignedIdentity!.outputs.clientId
+output AZURE_SEARCH_ENDPOINT string = searchServiceUpdate.outputs.endpoint
+output AZURE_CLIENT_ID string = userAssignedIdentity!.outputs.clientId
output AZURE_TENANT_ID string = tenant().tenantId
-output AZURE_AI_SEARCH_CONNECTION_NAME string = aiSearchConnectionName
+output AZURE_AI_SEARCH_CONNECTION_NAME string = aiSearchConnectionName
output AZURE_COGNITIVE_SERVICES string = 'https://cognitiveservices.azure.com/.default'
-output REASONING_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
+output REASONING_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
output MCP_SERVER_NAME string = 'MacaeMcpServer'
output MCP_SERVER_DESCRIPTION string = 'MCP server with greeting, HR, and planning tools'
output SUPPORTED_MODELS string = '["o3","o4-mini","gpt-4.1","gpt-4.1-mini"]'
-output AZURE_AI_SEARCH_API_KEY string = ''
output BACKEND_URL string = 'https://${containerApp.outputs.fqdn}'
+output AZURE_AI_PROJECT_ENDPOINT string = aiFoundryAiProjectEndpoint
+output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
+output AZURE_AI_AGENT_API_VERSION string = azureAiAgentAPIVersion
+output AZURE_AI_AGENT_PROJECT_CONNECTION_STRING string = '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
+output AZURE_DEV_COLLECT_TELEMETRY string = 'no'
+
+
+output AZURE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER string = storageContainerNameRetailCustomer
+output AZURE_STORAGE_CONTAINER_NAME_RETAIL_ORDER string = storageContainerNameRetailOrder
+output AZURE_STORAGE_CONTAINER_NAME_RFP_SUMMARY string = storageContainerNameRFPSummary
+output AZURE_STORAGE_CONTAINER_NAME_RFP_RISK string = storageContainerNameRFPRisk
+output AZURE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE string = storageContainerNameRFPCompliance
+output AZURE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY string = storageContainerNameContractSummary
+output AZURE_STORAGE_CONTAINER_NAME_CONTRACT_RISK string = storageContainerNameContractRisk
+output AZURE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE string = storageContainerNameContractCompliance
+output AZURE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER string = aiSearchIndexNameForRetailCustomer
+output AZURE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER string = aiSearchIndexNameForRetailOrder
+output AZURE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY string = aiSearchIndexNameForRFPSummary
+output AZURE_AI_SEARCH_INDEX_NAME_RFP_RISK string = aiSearchIndexNameForRFPRisk
+output AZURE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE string = aiSearchIndexNameForRFPCompliance
+output AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY string = aiSearchIndexNameForContractSummary
+output AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK string = aiSearchIndexNameForContractRisk
+output AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE string = aiSearchIndexNameForContractCompliance
+
+// Container Registry Outputs
+output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer
+output AZURE_CONTAINER_REGISTRY_NAME string = containerRegistry.outputs.name
+
diff --git a/infra/modules/aifp-connections.bicep b/infra/modules/aifp-connections.bicep
index 8afa883b3..25af63836 100644
--- a/infra/modules/aifp-connections.bicep
+++ b/infra/modules/aifp-connections.bicep
@@ -1,21 +1,27 @@
+@description('Name of the AI Foundry search connection')
param aifSearchConnectionName string
+
+@description('Name of the Azure AI Search service')
param searchServiceName string
+
+@description('Resource ID of the Azure AI Search service')
param searchServiceResourceId string
+
+@description('Location/region of the Azure AI Search service')
param searchServiceLocation string
+
+@description('Name of the AI Foundry account')
param aiFoundryName string
+
+@description('Name of the AI Foundry project')
param aiFoundryProjectName string
-@secure()
-param searchApiKey string
resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = {
name: '${aiFoundryName}/${aiFoundryProjectName}/${aifSearchConnectionName}'
properties: {
category: 'CognitiveSearch'
target: 'https://${searchServiceName}.search.windows.net'
- authType: 'ApiKey'
- credentials: {
- key: searchApiKey
- }
+ authType: 'AAD'
isSharedToAll: true
metadata: {
ApiType: 'Azure'
diff --git a/infra/modules/virtualNetwork.bicep b/infra/modules/virtualNetwork.bicep
index b9b5f11b6..6e54dd333 100644
--- a/infra/modules/virtualNetwork.bicep
+++ b/infra/modules/virtualNetwork.bicep
@@ -2,7 +2,7 @@
// Networking - NSGs, VNET and Subnets. Each subnet has its own NSG
/****************************************************************************************************************************/
@description('Name of the virtual network.')
-param name string
+param name string
@description('Azure region to deploy resources.')
param location string = resourceGroup().location
@@ -12,34 +12,32 @@ param addressPrefixes array
@description('An array of subnets to be created within the virtual network. Each subnet can have its own configuration and associated Network Security Group (NSG).')
param subnets subnetType[] = [
-
-
{
- name:'backend'
- addressPrefixes: ['10.0.0.0/27']
- networkSecurityGroup: {
- name: 'nsg-backend'
- securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
+ name: 'backend'
+ addressPrefixes: ['10.0.0.0/27']
+ networkSecurityGroup: {
+ name: 'nsg-backend'
+ securityRules: [
+ {
+ name: 'deny-hop-outbound'
+ properties: {
+ access: 'Deny'
+ destinationAddressPrefix: '*'
+ destinationPortRanges: [
+ '22'
+ '3389'
+ ]
+ direction: 'Outbound'
+ priority: 200
+ protocol: 'Tcp'
+ sourceAddressPrefix: 'VirtualNetwork'
+ sourcePortRange: '*'
+ }
}
- }
- ]
- }
+ ]
+ }
}
- {
+ {
name: 'containers'
addressPrefixes: ['10.0.2.0/23']
delegation: 'Microsoft.App/environments'
@@ -48,22 +46,22 @@ param subnets subnetType[] = [
networkSecurityGroup: {
name: 'nsg-containers'
securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
+ {
+ name: 'deny-hop-outbound'
+ properties: {
+ access: 'Deny'
+ destinationAddressPrefix: '*'
+ destinationPortRanges: [
+ '22'
+ '3389'
+ ]
+ direction: 'Outbound'
+ priority: 200
+ protocol: 'Tcp'
+ sourceAddressPrefix: 'VirtualNetwork'
+ sourcePortRange: '*'
+ }
}
- }
]
}
}
@@ -76,22 +74,22 @@ param subnets subnetType[] = [
networkSecurityGroup: {
name: 'nsg-webserverfarm'
securityRules: [
- {
- name: 'deny-hop-outbound'
- properties: {
- access: 'Deny'
- destinationAddressPrefix: '*'
- destinationPortRanges: [
- '22'
- '3389'
- ]
- direction: 'Outbound'
- priority: 200
- protocol: 'Tcp'
- sourceAddressPrefix: 'VirtualNetwork'
- sourcePortRange: '*'
+ {
+ name: 'deny-hop-outbound'
+ properties: {
+ access: 'Deny'
+ destinationAddressPrefix: '*'
+ destinationPortRanges: [
+ '22'
+ '3389'
+ ]
+ direction: 'Outbound'
+ priority: 200
+ protocol: 'Tcp'
+ sourceAddressPrefix: 'VirtualNetwork'
+ sourcePortRange: '*'
+ }
}
- }
]
}
}
@@ -199,9 +197,8 @@ param resourceSuffix string
// 1 B-series VMs (like Standard_B2ms) do not support accelerated networking.
// 2 Pick a VM size that does support accelerated networking (the usual jump-box candidates):
// Standard_DS2_v2 (2 vCPU, 7 GiB RAM, Premium SSD) // The most broadly available (itโs a legacy SKU supported in virtually every region).
-// Standard_D2s_v3 (2 vCPU, 8 GiB RAM, Premium SSD) // next most common
-// Standard_D2s_v4 (2 vCPU, 8 GiB RAM, Premium SSD) // Newest, so fewer regions availabl
-
+// Standard_D2s_v5 (2 vCPU, 8 GiB RAM, Premium SSD) // Current generation with better price-to-performance and wide availability
+// Standard_D2s_v4 (2 vCPU, 8 GiB RAM, Premium SSD) // Previous generation
// Subnet Classless Inter-Doman Routing (CIDR) Sizing Reference Table (Best Practices)
// | CIDR | # of Addresses | # of /24s | Notes |
@@ -307,11 +304,21 @@ output subnets subnetOutputType[] = [
]
// Dynamic outputs for individual subnets for backward compatibility
-output backendSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'backend') ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'backend')] : ''
-output containerSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'containers') ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'containers')] : ''
-output administrationSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'administration') ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'administration')] : ''
-output webserverfarmSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'webserverfarm') ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'webserverfarm')] : ''
-output bastionSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'AzureBastionSubnet') ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'AzureBastionSubnet')] : ''
+output backendSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'backend')
+ ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'backend')]
+ : ''
+output containerSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'containers')
+ ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'containers')]
+ : ''
+output administrationSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'administration')
+ ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'administration')]
+ : ''
+output webserverfarmSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'webserverfarm')
+ ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'webserverfarm')]
+ : ''
+output bastionSubnetResourceId string = contains(map(subnets, subnet => subnet.name), 'AzureBastionSubnet')
+ ? virtualNetwork.outputs.subnetResourceIds[indexOf(map(subnets, subnet => subnet.name), 'AzureBastionSubnet')]
+ : ''
@export()
@description('Custom type definition for subnet resource information as output')
@@ -335,8 +342,8 @@ type subnetType = {
@description('Required. The Name of the subnet resource.')
name: string
- @description('Required. Prefixes for the subnet.') // Required to ensure at least one prefix is provided
- addressPrefixes: string[]
+ @description('Required. Prefixes for the subnet.') // Required to ensure at least one prefix is provided
+ addressPrefixes: string[]
@description('Optional. The delegation to enable on the subnet.')
delegation: string?
diff --git a/infra/old/00-older/deploy_ai_foundry.bicep b/infra/old/00-older/deploy_ai_foundry.bicep
deleted file mode 100644
index 9f29af124..000000000
--- a/infra/old/00-older/deploy_ai_foundry.bicep
+++ /dev/null
@@ -1,316 +0,0 @@
-// Creates Azure dependent resources for Azure AI studio
-param solutionName string
-param solutionLocation string
-param keyVaultName string
-param gptModelName string
-param gptModelVersion string
-param managedIdentityObjectId string
-param aiServicesEndpoint string
-param aiServicesKey string
-param aiServicesId string
-
-// Load the abbrevations file required to name the azure resources.
-var abbrs = loadJsonContent('./abbreviations.json')
-
-var storageName = '${abbrs.storage.storageAccount}${solutionName}hub'
-var storageSkuName = 'Standard_LRS'
-var aiServicesName = '${abbrs.ai.aiServices}${solutionName}'
-var workspaceName = '${abbrs.managementGovernance.logAnalyticsWorkspace}${solutionName}hub'
-//var keyvaultName = '${abbrs.security.keyVault}${solutionName}'
-var location = solutionLocation
-var aiHubName = '${abbrs.ai.aiHub}${solutionName}'
-var aiHubFriendlyName = aiHubName
-var aiHubDescription = 'AI Hub for MACAE template'
-var aiProjectName = '${abbrs.ai.aiHubProject}${solutionName}'
-var aiProjectFriendlyName = aiProjectName
-var aiSearchName = '${abbrs.ai.aiSearch}${solutionName}'
-
-
-resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
- name: keyVaultName
-}
-
-resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
- name: workspaceName
- location: location
- tags: {}
- properties: {
- retentionInDays: 30
- sku: {
- name: 'PerGB2018'
- }
- }
-}
-
-
-var storageNameCleaned = replace(storageName, '-', '')
-
-
-resource storage 'Microsoft.Storage/storageAccounts@2022-09-01' = {
- name: storageNameCleaned
- location: location
- sku: {
- name: storageSkuName
- }
- kind: 'StorageV2'
- identity: {
- type: 'SystemAssigned'
- }
- properties: {
- accessTier: 'Hot'
- allowBlobPublicAccess: false
- allowCrossTenantReplication: false
- allowSharedKeyAccess: false
- encryption: {
- keySource: 'Microsoft.Storage'
- requireInfrastructureEncryption: false
- services: {
- blob: {
- enabled: true
- keyType: 'Account'
- }
- file: {
- enabled: true
- keyType: 'Account'
- }
- queue: {
- enabled: true
- keyType: 'Service'
- }
- table: {
- enabled: true
- keyType: 'Service'
- }
- }
- }
- isHnsEnabled: false
- isNfsV3Enabled: false
- keyPolicy: {
- keyExpirationPeriodInDays: 7
- }
- largeFileSharesState: 'Disabled'
- minimumTlsVersion: 'TLS1_2'
- networkAcls: {
- bypass: 'AzureServices'
- defaultAction: 'Allow'
- }
- supportsHttpsTrafficOnly: true
- }
-}
-
-@description('This is the built-in Storage Blob Data Contributor.')
-resource blobDataContributor 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
- scope: subscription()
- name: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
-}
-
-resource storageroleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(resourceGroup().id, managedIdentityObjectId, blobDataContributor.id)
- scope: storage
- properties: {
- principalId: managedIdentityObjectId
- roleDefinitionId: blobDataContributor.id
- principalType: 'ServicePrincipal'
- }
-}
-
-resource aiHub 'Microsoft.MachineLearningServices/workspaces@2023-08-01-preview' = {
- name: aiHubName
- location: location
- identity: {
- type: 'SystemAssigned'
- }
- properties: {
- // organization
- friendlyName: aiHubFriendlyName
- description: aiHubDescription
-
- // dependent resources
- keyVault: keyVault.id
- storageAccount: storage.id
- }
- kind: 'hub'
-
- resource aiServicesConnection 'connections@2024-07-01-preview' = {
- name: '${aiHubName}-connection-AzureOpenAI'
- properties: {
- category: 'AIServices'
- target: aiServicesEndpoint
- authType: 'AAD'
- isSharedToAll: true
- metadata: {
- ApiType: 'Azure'
- ResourceId: aiServicesId
- }
- }
- }
-}
-
-resource aiHubProject 'Microsoft.MachineLearningServices/workspaces@2024-01-01-preview' = {
- name: aiProjectName
- location: location
- kind: 'Project'
- identity: {
- type: 'SystemAssigned'
- }
- properties: {
- friendlyName: aiProjectFriendlyName
- hubResourceId: aiHub.id
- }
-}
-
-resource aiDeveloper 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
- name: '64702f94-c441-49e6-a78b-ef80e0188fee'
-}
-
-resource aiDevelopertoAIProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(aiHubProject.id, aiDeveloper.id)
- scope: resourceGroup()
- properties: {
- roleDefinitionId: aiDeveloper.id
- principalId: aiHubProject.identity.principalId
- principalType: 'ServicePrincipal'
- }
-}
-
-resource tenantIdEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'TENANT-ID'
- properties: {
- value: subscription().tenantId
- }
-}
-
-resource azureOpenAIInferenceEndpoint 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPENAI-INFERENCE-ENDPOINT'
- properties: {
- value:''
- }
-}
-
-resource azureOpenAIInferenceKey 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPENAI-INFERENCE-KEY'
- properties: {
- value:''
- }
-}
-
-resource azureOpenAIApiKeyEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPENAI-KEY'
- properties: {
- value: aiServicesKey //aiServices_m.listKeys().key1
- }
-}
-
-resource azureOpenAIDeploymentModel 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPEN-AI-DEPLOYMENT-MODEL'
- properties: {
- value: gptModelName
- }
-}
-
-resource azureOpenAIApiVersionEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPENAI-PREVIEW-API-VERSION'
- properties: {
- value: gptModelVersion //'2024-02-15-preview'
- }
-}
-
-resource azureOpenAIEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPENAI-ENDPOINT'
- properties: {
- value: aiServicesEndpoint//aiServices_m.properties.endpoint
- }
-}
-
-resource azureAIProjectConnectionStringEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-AI-PROJECT-CONN-STRING'
- properties: {
- value: '${split(aiHubProject.properties.discoveryUrl, '/')[2]};${subscription().subscriptionId};${resourceGroup().name};${aiHubProject.name}'
- }
-}
-
-resource azureOpenAICUApiVersionEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-OPENAI-CU-VERSION'
- properties: {
- value: '?api-version=2024-12-01-preview'
- }
-}
-
-resource azureSearchIndexEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-SEARCH-INDEX'
- properties: {
- value: 'transcripts_index'
- }
-}
-
-resource cogServiceEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'COG-SERVICES-ENDPOINT'
- properties: {
- value: aiServicesEndpoint
- }
-}
-
-resource cogServiceKeyEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'COG-SERVICES-KEY'
- properties: {
- value: aiServicesKey
- }
-}
-
-resource cogServiceNameEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'COG-SERVICES-NAME'
- properties: {
- value: aiServicesName
- }
-}
-
-resource azureSubscriptionIdEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-SUBSCRIPTION-ID'
- properties: {
- value: subscription().subscriptionId
- }
-}
-
-resource resourceGroupNameEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-RESOURCE-GROUP'
- properties: {
- value: resourceGroup().name
- }
-}
-
-resource azureLocatioEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
- parent: keyVault
- name: 'AZURE-LOCATION'
- properties: {
- value: solutionLocation
- }
-}
-
-output keyvaultName string = keyVaultName
-output keyvaultId string = keyVault.id
-
-output aiServicesName string = aiServicesName
-output aiSearchName string = aiSearchName
-output aiProjectName string = aiHubProject.name
-
-output storageAccountName string = storageNameCleaned
-
-output logAnalyticsId string = logAnalytics.id
-output storageAccountId string = storage.id
-
-output projectConnectionString string = '${split(aiHubProject.properties.discoveryUrl, '/')[2]};${subscription().subscriptionId};${resourceGroup().name};${aiHubProject.name}'
diff --git a/infra/old/00-older/deploy_keyvault.bicep b/infra/old/00-older/deploy_keyvault.bicep
deleted file mode 100644
index 3a5c1f761..000000000
--- a/infra/old/00-older/deploy_keyvault.bicep
+++ /dev/null
@@ -1,62 +0,0 @@
-param solutionLocation string
-param managedIdentityObjectId string
-
-@description('KeyVault Name')
-param keyvaultName string
-
-resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
- name: keyvaultName
- location: solutionLocation
- properties: {
- createMode: 'default'
- accessPolicies: [
- {
- objectId: managedIdentityObjectId
- permissions: {
- certificates: [
- 'all'
- ]
- keys: [
- 'all'
- ]
- secrets: [
- 'all'
- ]
- storage: [
- 'all'
- ]
- }
- tenantId: subscription().tenantId
- }
- ]
- enabledForDeployment: true
- enabledForDiskEncryption: true
- enabledForTemplateDeployment: true
- enableRbacAuthorization: true
- publicNetworkAccess: 'enabled'
- sku: {
- family: 'A'
- name: 'standard'
- }
- softDeleteRetentionInDays: 7
- tenantId: subscription().tenantId
- }
-}
-
-@description('This is the built-in Key Vault Administrator role.')
-resource kvAdminRole 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
- scope: resourceGroup()
- name: '00482a5a-887f-4fb3-b363-3b7fe8e74483'
-}
-
-resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(resourceGroup().id, managedIdentityObjectId, kvAdminRole.id)
- properties: {
- principalId: managedIdentityObjectId
- roleDefinitionId:kvAdminRole.id
- principalType: 'ServicePrincipal'
- }
-}
-
-output keyvaultName string = keyvaultName
-output keyvaultId string = keyVault.id
diff --git a/infra/old/00-older/deploy_managed_identity.bicep b/infra/old/00-older/deploy_managed_identity.bicep
deleted file mode 100644
index 5288872cb..000000000
--- a/infra/old/00-older/deploy_managed_identity.bicep
+++ /dev/null
@@ -1,45 +0,0 @@
-// ========== Managed Identity ========== //
-targetScope = 'resourceGroup'
-
-@description('Solution Location')
-//param solutionLocation string
-param managedIdentityId string
-param managedIdentityPropPrin string
-param managedIdentityLocation string
-@description('Managed Identity Name')
-param miName string
-
-// resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
-// name: miName
-// location: solutionLocation
-// tags: {
-// app: solutionName
-// location: solutionLocation
-// }
-// }
-
-@description('This is the built-in owner role. See https://docs.microsoft.com/azure/role-based-access-control/built-in-roles#owner')
-resource ownerRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
- scope: resourceGroup()
- name: '8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
-}
-
-resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(resourceGroup().id, managedIdentityId, ownerRoleDefinition.id)
- properties: {
- principalId: managedIdentityPropPrin
- roleDefinitionId: ownerRoleDefinition.id
- principalType: 'ServicePrincipal'
- }
-}
-
-
-output managedIdentityOutput object = {
- id: managedIdentityId
- objectId: managedIdentityPropPrin
- resourceId: managedIdentityId
- location: managedIdentityLocation
- name: miName
-}
-
-output managedIdentityId string = managedIdentityId
diff --git a/infra/old/00-older/macae-continer-oc.json b/infra/old/00-older/macae-continer-oc.json
deleted file mode 100644
index 40c676ebe..000000000
--- a/infra/old/00-older/macae-continer-oc.json
+++ /dev/null
@@ -1,458 +0,0 @@
-{
- "$schema": "https://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": "9524414973084491660"
- }
- },
- "parameters": {
- "location": {
- "type": "string",
- "defaultValue": "EastUS2",
- "metadata": {
- "description": "Location for all resources."
- }
- },
- "azureOpenAILocation": {
- "type": "string",
- "defaultValue": "EastUS",
- "metadata": {
- "description": "Location for OpenAI resources."
- }
- },
- "prefix": {
- "type": "string",
- "defaultValue": "macae",
- "metadata": {
- "description": "A prefix to add to the start of all resource names. Note: A \"unique\" suffix will also be added"
- }
- },
- "tags": {
- "type": "object",
- "defaultValue": {},
- "metadata": {
- "description": "Tags to apply to all deployed resources"
- }
- },
- "resourceSize": {
- "type": "object",
- "properties": {
- "gpt4oCapacity": {
- "type": "int"
- },
- "containerAppSize": {
- "type": "object",
- "properties": {
- "cpu": {
- "type": "string"
- },
- "memory": {
- "type": "string"
- },
- "minReplicas": {
- "type": "int"
- },
- "maxReplicas": {
- "type": "int"
- }
- }
- }
- },
- "defaultValue": {
- "gpt4oCapacity": 50,
- "containerAppSize": {
- "cpu": "2.0",
- "memory": "4.0Gi",
- "minReplicas": 1,
- "maxReplicas": 1
- }
- },
- "metadata": {
- "description": "The size of the resources to deploy, defaults to a mini size"
- }
- }
- },
- "variables": {
- "appVersion": "latest",
- "resgistryName": "biabcontainerreg",
- "dockerRegistryUrl": "[format('https://{0}.azurecr.io', variables('resgistryName'))]",
- "backendDockerImageURL": "[format('{0}.azurecr.io/macaebackend:{1}', variables('resgistryName'), variables('appVersion'))]",
- "frontendDockerImageURL": "[format('{0}.azurecr.io/macaefrontend:{1}', variables('resgistryName'), variables('appVersion'))]",
- "uniqueNameFormat": "[format('{0}-{{0}}-{1}', parameters('prefix'), uniqueString(resourceGroup().id, parameters('prefix')))]",
- "aoaiApiVersion": "2024-08-01-preview"
- },
- "resources": {
- "openai::gpt4o": {
- "type": "Microsoft.CognitiveServices/accounts/deployments",
- "apiVersion": "2023-10-01-preview",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'openai'), 'gpt-4o')]",
- "sku": {
- "name": "GlobalStandard",
- "capacity": "[parameters('resourceSize').gpt4oCapacity]"
- },
- "properties": {
- "model": {
- "format": "OpenAI",
- "name": "gpt-4o",
- "version": "2024-08-06"
- },
- "versionUpgradeOption": "NoAutoUpgrade"
- },
- "dependsOn": [
- "openai"
- ]
- },
- "cosmos::autogenDb::memoryContainer": {
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}/{2}', format(variables('uniqueNameFormat'), 'cosmos'), 'autogen', 'memory')]",
- "properties": {
- "resource": {
- "id": "memory",
- "partitionKey": {
- "kind": "Hash",
- "version": 2,
- "paths": [
- "/session_id"
- ]
- }
- }
- },
- "dependsOn": [
- "cosmos::autogenDb"
- ]
- },
- "cosmos::contributorRoleDefinition": {
- "existing": true,
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002')]",
- "dependsOn": [
- "cosmos"
- ]
- },
- "cosmos::autogenDb": {
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'cosmos'), 'autogen')]",
- "properties": {
- "resource": {
- "id": "autogen",
- "createMode": "Default"
- }
- },
- "dependsOn": [
- "cosmos"
- ]
- },
- "containerAppEnv::aspireDashboard": {
- "type": "Microsoft.App/managedEnvironments/dotNetComponents",
- "apiVersion": "2024-02-02-preview",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'containerapp'), 'aspire-dashboard')]",
- "properties": {
- "componentType": "AspireDashboard"
- },
- "dependsOn": [
- "containerAppEnv"
- ]
- },
- "logAnalytics": {
- "type": "Microsoft.OperationalInsights/workspaces",
- "apiVersion": "2023-09-01",
- "name": "[format(variables('uniqueNameFormat'), 'logs')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "properties": {
- "retentionInDays": 30,
- "sku": {
- "name": "PerGB2018"
- }
- }
- },
- "appInsights": {
- "type": "Microsoft.Insights/components",
- "apiVersion": "2020-02-02-preview",
- "name": "[format(variables('uniqueNameFormat'), 'appins')]",
- "location": "[parameters('location')]",
- "kind": "web",
- "properties": {
- "Application_Type": "web",
- "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', format(variables('uniqueNameFormat'), 'logs'))]"
- },
- "dependsOn": [
- "logAnalytics"
- ]
- },
- "openai": {
- "type": "Microsoft.CognitiveServices/accounts",
- "apiVersion": "2023-10-01-preview",
- "name": "[format(variables('uniqueNameFormat'), 'openai')]",
- "location": "[parameters('azureOpenAILocation')]",
- "tags": "[parameters('tags')]",
- "kind": "OpenAI",
- "sku": {
- "name": "S0"
- },
- "properties": {
- "customSubDomainName": "[format(variables('uniqueNameFormat'), 'openai')]"
- }
- },
- "aoaiUserRoleDefinition": {
- "existing": true,
- "type": "Microsoft.Authorization/roleDefinitions",
- "apiVersion": "2022-05-01-preview",
- "name": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd"
- },
- "acaAoaiRoleAssignment": {
- "type": "Microsoft.Authorization/roleAssignments",
- "apiVersion": "2022-04-01",
- "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', format(variables('uniqueNameFormat'), 'openai'))]",
- "name": "[guid(resourceId('Microsoft.App/containerApps', format('{0}-backend', parameters('prefix'))), resourceId('Microsoft.CognitiveServices/accounts', format(variables('uniqueNameFormat'), 'openai')), resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'))]",
- "properties": {
- "principalId": "[reference('containerApp', '2024-03-01', 'full').identity.principalId]",
- "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]",
- "principalType": "ServicePrincipal"
- },
- "dependsOn": [
- "containerApp",
- "openai"
- ]
- },
- "cosmos": {
- "type": "Microsoft.DocumentDB/databaseAccounts",
- "apiVersion": "2024-05-15",
- "name": "[format(variables('uniqueNameFormat'), 'cosmos')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "kind": "GlobalDocumentDB",
- "properties": {
- "databaseAccountOfferType": "Standard",
- "enableFreeTier": false,
- "locations": [
- {
- "failoverPriority": 0,
- "locationName": "[parameters('location')]"
- }
- ],
- "capabilities": [
- {
- "name": "EnableServerless"
- }
- ]
- }
- },
- "pullIdentity": {
- "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
- "apiVersion": "2023-07-31-preview",
- "name": "[format(variables('uniqueNameFormat'), 'containerapp-pull')]",
- "location": "[parameters('location')]"
- },
- "containerAppEnv": {
- "type": "Microsoft.App/managedEnvironments",
- "apiVersion": "2024-03-01",
- "name": "[format(variables('uniqueNameFormat'), 'containerapp')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "properties": {
- "daprAIConnectionString": "[reference('appInsights').ConnectionString]",
- "appLogsConfiguration": {
- "destination": "log-analytics",
- "logAnalyticsConfiguration": {
- "customerId": "[reference('logAnalytics').customerId]",
- "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', format(variables('uniqueNameFormat'), 'logs')), '2023-09-01').primarySharedKey]"
- }
- }
- },
- "dependsOn": [
- "appInsights",
- "logAnalytics"
- ]
- },
- "acaCosomsRoleAssignment": {
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'cosmos'), guid(resourceId('Microsoft.App/containerApps', format('{0}-backend', parameters('prefix'))), resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002')))]",
- "properties": {
- "principalId": "[reference('containerApp', '2024-03-01', 'full').identity.principalId]",
- "roleDefinitionId": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002')]",
- "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', format(variables('uniqueNameFormat'), 'cosmos'))]"
- },
- "dependsOn": [
- "containerApp",
- "cosmos"
- ]
- },
- "containerApp": {
- "type": "Microsoft.App/containerApps",
- "apiVersion": "2024-03-01",
- "name": "[format('{0}-backend', parameters('prefix'))]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "identity": {
- "type": "SystemAssigned, UserAssigned",
- "userAssignedIdentities": {
- "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format(variables('uniqueNameFormat'), 'containerapp-pull')))]": {}
- }
- },
- "properties": {
- "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', format(variables('uniqueNameFormat'), 'containerapp'))]",
- "configuration": {
- "ingress": {
- "targetPort": 8000,
- "external": true,
- "corsPolicy": {
- "allowedOrigins": [
- "[format('https://{0}.azurewebsites.net', format(variables('uniqueNameFormat'), 'frontend'))]",
- "[format('http://{0}.azurewebsites.net', format(variables('uniqueNameFormat'), 'frontend'))]"
- ]
- }
- },
- "activeRevisionsMode": "Single"
- },
- "template": {
- "scale": {
- "minReplicas": "[parameters('resourceSize').containerAppSize.minReplicas]",
- "maxReplicas": "[parameters('resourceSize').containerAppSize.maxReplicas]",
- "rules": [
- {
- "name": "http-scaler",
- "http": {
- "metadata": {
- "concurrentRequests": "100"
- }
- }
- }
- ]
- },
- "containers": [
- {
- "name": "backend",
- "image": "[variables('backendDockerImageURL')]",
- "resources": {
- "cpu": "[json(parameters('resourceSize').containerAppSize.cpu)]",
- "memory": "[parameters('resourceSize').containerAppSize.memory]"
- },
- "env": [
- {
- "name": "COSMOSDB_ENDPOINT",
- "value": "[reference('cosmos').documentEndpoint]"
- },
- {
- "name": "COSMOSDB_DATABASE",
- "value": "autogen"
- },
- {
- "name": "COSMOSDB_CONTAINER",
- "value": "memory"
- },
- {
- "name": "AZURE_OPENAI_ENDPOINT",
- "value": "[reference('openai').endpoint]"
- },
- {
- "name": "AZURE_OPENAI_DEPLOYMENT_NAME",
- "value": "gpt-4o"
- },
- {
- "name": "AZURE_OPENAI_API_VERSION",
- "value": "[variables('aoaiApiVersion')]"
- },
- {
- "name": "FRONTEND_SITE_NAME",
- "value": "[format('https://{0}.azurewebsites.net', format(variables('uniqueNameFormat'), 'frontend'))]"
- },
- {
- "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
- "value": "[reference('appInsights').ConnectionString]"
- }
- ]
- }
- ]
- }
- },
- "dependsOn": [
- "appInsights",
- "cosmos::autogenDb",
- "containerAppEnv",
- "cosmos",
- "openai::gpt4o",
- "cosmos::autogenDb::memoryContainer",
- "openai",
- "pullIdentity"
- ],
- "metadata": {
- "description": ""
- }
- },
- "frontendAppServicePlan": {
- "type": "Microsoft.Web/serverfarms",
- "apiVersion": "2021-02-01",
- "name": "[format(variables('uniqueNameFormat'), 'frontend-plan')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "sku": {
- "name": "P1v2",
- "capacity": 1,
- "tier": "PremiumV2"
- },
- "properties": {
- "reserved": true
- },
- "kind": "linux"
- },
- "frontendAppService": {
- "type": "Microsoft.Web/sites",
- "apiVersion": "2021-02-01",
- "name": "[format(variables('uniqueNameFormat'), 'frontend')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "kind": "app,linux,container",
- "properties": {
- "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', format(variables('uniqueNameFormat'), 'frontend-plan'))]",
- "reserved": true,
- "siteConfig": {
- "linuxFxVersion": "[format('DOCKER|{0}', variables('frontendDockerImageURL'))]",
- "appSettings": [
- {
- "name": "DOCKER_REGISTRY_SERVER_URL",
- "value": "[variables('dockerRegistryUrl')]"
- },
- {
- "name": "WEBSITES_PORT",
- "value": "3000"
- },
- {
- "name": "WEBSITES_CONTAINER_START_TIME_LIMIT",
- "value": "1800"
- },
- {
- "name": "BACKEND_API_URL",
- "value": "[format('https://{0}', reference('containerApp').configuration.ingress.fqdn)]"
- }
- ]
- }
- },
- "identity": {
- "type": "SystemAssigned,UserAssigned",
- "userAssignedIdentities": {
- "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format(variables('uniqueNameFormat'), 'containerapp-pull')))]": {}
- }
- },
- "dependsOn": [
- "containerApp",
- "frontendAppServicePlan",
- "pullIdentity"
- ]
- }
- },
- "outputs": {
- "cosmosAssignCli": {
- "type": "string",
- "value": "[format('az cosmosdb sql role assignment create --resource-group \"{0}\" --account-name \"{1}\" --role-definition-id \"{2}\" --scope \"{3}\" --principal-id \"fill-in\"', resourceGroup().name, format(variables('uniqueNameFormat'), 'cosmos'), resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002'), resourceId('Microsoft.DocumentDB/databaseAccounts', format(variables('uniqueNameFormat'), 'cosmos')))]"
- }
- }
-}
\ No newline at end of file
diff --git a/infra/old/00-older/macae-continer.json b/infra/old/00-older/macae-continer.json
deleted file mode 100644
index db8539188..000000000
--- a/infra/old/00-older/macae-continer.json
+++ /dev/null
@@ -1,458 +0,0 @@
-{
- "$schema": "https://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": "8201361287909347586"
- }
- },
- "parameters": {
- "location": {
- "type": "string",
- "defaultValue": "EastUS2",
- "metadata": {
- "description": "Location for all resources."
- }
- },
- "azureOpenAILocation": {
- "type": "string",
- "defaultValue": "EastUS",
- "metadata": {
- "description": "Location for OpenAI resources."
- }
- },
- "prefix": {
- "type": "string",
- "defaultValue": "macae",
- "metadata": {
- "description": "A prefix to add to the start of all resource names. Note: A \"unique\" suffix will also be added"
- }
- },
- "tags": {
- "type": "object",
- "defaultValue": {},
- "metadata": {
- "description": "Tags to apply to all deployed resources"
- }
- },
- "resourceSize": {
- "type": "object",
- "properties": {
- "gpt4oCapacity": {
- "type": "int"
- },
- "containerAppSize": {
- "type": "object",
- "properties": {
- "cpu": {
- "type": "string"
- },
- "memory": {
- "type": "string"
- },
- "minReplicas": {
- "type": "int"
- },
- "maxReplicas": {
- "type": "int"
- }
- }
- }
- },
- "defaultValue": {
- "gpt4oCapacity": 50,
- "containerAppSize": {
- "cpu": "2.0",
- "memory": "4.0Gi",
- "minReplicas": 1,
- "maxReplicas": 1
- }
- },
- "metadata": {
- "description": "The size of the resources to deploy, defaults to a mini size"
- }
- }
- },
- "variables": {
- "appVersion": "latest",
- "resgistryName": "biabcontainerreg",
- "dockerRegistryUrl": "[format('https://{0}.azurecr.io', variables('resgistryName'))]",
- "backendDockerImageURL": "[format('{0}.azurecr.io/macaebackend:{1}', variables('resgistryName'), variables('appVersion'))]",
- "frontendDockerImageURL": "[format('{0}.azurecr.io/macaefrontend:{1}', variables('resgistryName'), variables('appVersion'))]",
- "uniqueNameFormat": "[format('{0}-{{0}}-{1}', parameters('prefix'), uniqueString(resourceGroup().id, parameters('prefix')))]",
- "aoaiApiVersion": "2024-08-01-preview"
- },
- "resources": {
- "openai::gpt4o": {
- "type": "Microsoft.CognitiveServices/accounts/deployments",
- "apiVersion": "2023-10-01-preview",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'openai'), 'gpt-4o')]",
- "sku": {
- "name": "GlobalStandard",
- "capacity": "[parameters('resourceSize').gpt4oCapacity]"
- },
- "properties": {
- "model": {
- "format": "OpenAI",
- "name": "gpt-4o",
- "version": "2024-08-06"
- },
- "versionUpgradeOption": "NoAutoUpgrade"
- },
- "dependsOn": [
- "openai"
- ]
- },
- "cosmos::autogenDb::memoryContainer": {
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}/{2}', format(variables('uniqueNameFormat'), 'cosmos'), 'autogen', 'memory')]",
- "properties": {
- "resource": {
- "id": "memory",
- "partitionKey": {
- "kind": "Hash",
- "version": 2,
- "paths": [
- "/session_id"
- ]
- }
- }
- },
- "dependsOn": [
- "cosmos::autogenDb"
- ]
- },
- "cosmos::contributorRoleDefinition": {
- "existing": true,
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002')]",
- "dependsOn": [
- "cosmos"
- ]
- },
- "cosmos::autogenDb": {
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'cosmos'), 'autogen')]",
- "properties": {
- "resource": {
- "id": "autogen",
- "createMode": "Default"
- }
- },
- "dependsOn": [
- "cosmos"
- ]
- },
- "containerAppEnv::aspireDashboard": {
- "type": "Microsoft.App/managedEnvironments/dotNetComponents",
- "apiVersion": "2024-02-02-preview",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'containerapp'), 'aspire-dashboard')]",
- "properties": {
- "componentType": "AspireDashboard"
- },
- "dependsOn": [
- "containerAppEnv"
- ]
- },
- "logAnalytics": {
- "type": "Microsoft.OperationalInsights/workspaces",
- "apiVersion": "2023-09-01",
- "name": "[format(variables('uniqueNameFormat'), 'logs')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "properties": {
- "retentionInDays": 30,
- "sku": {
- "name": "PerGB2018"
- }
- }
- },
- "appInsights": {
- "type": "Microsoft.Insights/components",
- "apiVersion": "2020-02-02-preview",
- "name": "[format(variables('uniqueNameFormat'), 'appins')]",
- "location": "[parameters('location')]",
- "kind": "web",
- "properties": {
- "Application_Type": "web",
- "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', format(variables('uniqueNameFormat'), 'logs'))]"
- },
- "dependsOn": [
- "logAnalytics"
- ]
- },
- "openai": {
- "type": "Microsoft.CognitiveServices/accounts",
- "apiVersion": "2023-10-01-preview",
- "name": "[format(variables('uniqueNameFormat'), 'openai')]",
- "location": "[parameters('azureOpenAILocation')]",
- "tags": "[parameters('tags')]",
- "kind": "OpenAI",
- "sku": {
- "name": "S0"
- },
- "properties": {
- "customSubDomainName": "[format(variables('uniqueNameFormat'), 'openai')]"
- }
- },
- "aoaiUserRoleDefinition": {
- "existing": true,
- "type": "Microsoft.Authorization/roleDefinitions",
- "apiVersion": "2022-05-01-preview",
- "name": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd"
- },
- "acaAoaiRoleAssignment": {
- "type": "Microsoft.Authorization/roleAssignments",
- "apiVersion": "2022-04-01",
- "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', format(variables('uniqueNameFormat'), 'openai'))]",
- "name": "[guid(resourceId('Microsoft.App/containerApps', format('{0}-backend', parameters('prefix'))), resourceId('Microsoft.CognitiveServices/accounts', format(variables('uniqueNameFormat'), 'openai')), resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'))]",
- "properties": {
- "principalId": "[reference('containerApp', '2024-03-01', 'full').identity.principalId]",
- "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]",
- "principalType": "ServicePrincipal"
- },
- "dependsOn": [
- "containerApp",
- "openai"
- ]
- },
- "cosmos": {
- "type": "Microsoft.DocumentDB/databaseAccounts",
- "apiVersion": "2024-05-15",
- "name": "[format(variables('uniqueNameFormat'), 'cosmos')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "kind": "GlobalDocumentDB",
- "properties": {
- "databaseAccountOfferType": "Standard",
- "enableFreeTier": false,
- "locations": [
- {
- "failoverPriority": 0,
- "locationName": "[parameters('location')]"
- }
- ],
- "capabilities": [
- {
- "name": "EnableServerless"
- }
- ]
- }
- },
- "pullIdentity": {
- "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
- "apiVersion": "2023-07-31-preview",
- "name": "[format(variables('uniqueNameFormat'), 'containerapp-pull')]",
- "location": "[parameters('location')]"
- },
- "containerAppEnv": {
- "type": "Microsoft.App/managedEnvironments",
- "apiVersion": "2024-03-01",
- "name": "[format(variables('uniqueNameFormat'), 'containerapp')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "properties": {
- "daprAIConnectionString": "[reference('appInsights').ConnectionString]",
- "appLogsConfiguration": {
- "destination": "log-analytics",
- "logAnalyticsConfiguration": {
- "customerId": "[reference('logAnalytics').customerId]",
- "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', format(variables('uniqueNameFormat'), 'logs')), '2023-09-01').primarySharedKey]"
- }
- }
- },
- "dependsOn": [
- "appInsights",
- "logAnalytics"
- ]
- },
- "acaCosomsRoleAssignment": {
- "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments",
- "apiVersion": "2024-05-15",
- "name": "[format('{0}/{1}', format(variables('uniqueNameFormat'), 'cosmos'), guid(resourceId('Microsoft.App/containerApps', format('{0}-backend', parameters('prefix'))), resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002')))]",
- "properties": {
- "principalId": "[reference('containerApp', '2024-03-01', 'full').identity.principalId]",
- "roleDefinitionId": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002')]",
- "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', format(variables('uniqueNameFormat'), 'cosmos'))]"
- },
- "dependsOn": [
- "containerApp",
- "cosmos"
- ]
- },
- "containerApp": {
- "type": "Microsoft.App/containerApps",
- "apiVersion": "2024-03-01",
- "name": "[format('{0}-backend', parameters('prefix'))]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "identity": {
- "type": "SystemAssigned, UserAssigned",
- "userAssignedIdentities": {
- "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format(variables('uniqueNameFormat'), 'containerapp-pull')))]": {}
- }
- },
- "properties": {
- "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', format(variables('uniqueNameFormat'), 'containerapp'))]",
- "configuration": {
- "ingress": {
- "targetPort": 8000,
- "external": true,
- "corsPolicy": {
- "allowedOrigins": [
- "[format('https://{0}.azurewebsites.net', format(variables('uniqueNameFormat'), 'frontend'))]",
- "[format('http://{0}.azurewebsites.net', format(variables('uniqueNameFormat'), 'frontend'))]"
- ]
- }
- },
- "activeRevisionsMode": "Single"
- },
- "template": {
- "scale": {
- "minReplicas": "[parameters('resourceSize').containerAppSize.minReplicas]",
- "maxReplicas": "[parameters('resourceSize').containerAppSize.maxReplicas]",
- "rules": [
- {
- "name": "http-scaler",
- "http": {
- "metadata": {
- "concurrentRequests": "100"
- }
- }
- }
- ]
- },
- "containers": [
- {
- "name": "backend",
- "image": "[variables('backendDockerImageURL')]",
- "resources": {
- "cpu": "[json(parameters('resourceSize').containerAppSize.cpu)]",
- "memory": "[parameters('resourceSize').containerAppSize.memory]"
- },
- "env": [
- {
- "name": "COSMOSDB_ENDPOINT",
- "value": "[reference('cosmos').documentEndpoint]"
- },
- {
- "name": "COSMOSDB_DATABASE",
- "value": "autogen"
- },
- {
- "name": "COSMOSDB_CONTAINER",
- "value": "memory"
- },
- {
- "name": "AZURE_OPENAI_ENDPOINT",
- "value": "[reference('openai').endpoint]"
- },
- {
- "name": "AZURE_OPENAI_DEPLOYMENT_NAME",
- "value": "gpt-4o"
- },
- {
- "name": "AZURE_OPENAI_API_VERSION",
- "value": "[variables('aoaiApiVersion')]"
- },
- {
- "name": "FRONTEND_SITE_NAME",
- "value": "[format('https://{0}.azurewebsites.net', format(variables('uniqueNameFormat'), 'frontend'))]"
- },
- {
- "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
- "value": "[reference('appInsights').ConnectionString]"
- }
- ]
- }
- ]
- }
- },
- "dependsOn": [
- "appInsights",
- "containerAppEnv",
- "cosmos",
- "cosmos::autogenDb",
- "cosmos::autogenDb::memoryContainer",
- "openai",
- "openai::gpt4o",
- "pullIdentity"
- ],
- "metadata": {
- "description": ""
- }
- },
- "frontendAppServicePlan": {
- "type": "Microsoft.Web/serverfarms",
- "apiVersion": "2021-02-01",
- "name": "[format(variables('uniqueNameFormat'), 'frontend-plan')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "sku": {
- "name": "P1v2",
- "capacity": 1,
- "tier": "PremiumV2"
- },
- "properties": {
- "reserved": true
- },
- "kind": "linux"
- },
- "frontendAppService": {
- "type": "Microsoft.Web/sites",
- "apiVersion": "2021-02-01",
- "name": "[format(variables('uniqueNameFormat'), 'frontend')]",
- "location": "[parameters('location')]",
- "tags": "[parameters('tags')]",
- "kind": "app,linux,container",
- "properties": {
- "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', format(variables('uniqueNameFormat'), 'frontend-plan'))]",
- "reserved": true,
- "siteConfig": {
- "linuxFxVersion": "[format('DOCKER|{0}', variables('frontendDockerImageURL'))]",
- "appSettings": [
- {
- "name": "DOCKER_REGISTRY_SERVER_URL",
- "value": "[variables('dockerRegistryUrl')]"
- },
- {
- "name": "WEBSITES_PORT",
- "value": "3000"
- },
- {
- "name": "WEBSITES_CONTAINER_START_TIME_LIMIT",
- "value": "1800"
- },
- {
- "name": "BACKEND_API_URL",
- "value": "[format('https://{0}', reference('containerApp').configuration.ingress.fqdn)]"
- }
- ]
- }
- },
- "identity": {
- "type": "SystemAssigned,UserAssigned",
- "userAssignedIdentities": {
- "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format(variables('uniqueNameFormat'), 'containerapp-pull')))]": {}
- }
- },
- "dependsOn": [
- "containerApp",
- "frontendAppServicePlan",
- "pullIdentity"
- ]
- }
- },
- "outputs": {
- "cosmosAssignCli": {
- "type": "string",
- "value": "[format('az cosmosdb sql role assignment create --resource-group \"{0}\" --account-name \"{1}\" --role-definition-id \"{2}\" --scope \"{3}\" --principal-id \"fill-in\"', resourceGroup().name, format(variables('uniqueNameFormat'), 'cosmos'), resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', format(variables('uniqueNameFormat'), 'cosmos'), '00000000-0000-0000-0000-000000000002'), resourceId('Microsoft.DocumentDB/databaseAccounts', format(variables('uniqueNameFormat'), 'cosmos')))]"
- }
- }
-}
\ No newline at end of file
diff --git a/infra/old/00-older/macae-dev.bicep b/infra/old/00-older/macae-dev.bicep
deleted file mode 100644
index 5157fa92f..000000000
--- a/infra/old/00-older/macae-dev.bicep
+++ /dev/null
@@ -1,131 +0,0 @@
-@description('Location for all resources.')
-param location string = resourceGroup().location
-
-@description('location for Cosmos DB resources.')
-// prompt for this as there is often quota restrictions
-param cosmosLocation string
-
-@description('Location for OpenAI resources.')
-// prompt for this as there is often quota restrictions
-param azureOpenAILocation string
-
-@description('A prefix to add to the start of all resource names. Note: A "unique" suffix will also be added')
-param prefix string = 'macae'
-
-@description('Tags to apply to all deployed resources')
-param tags object = {}
-
-@description('Principal ID to assign to the Cosmos DB contributor & Azure OpenAI user role, leave empty to skip role assignment. This is your ObjectID wihtin Entra ID.')
-param developerPrincipalId string
-
-var uniqueNameFormat = '${prefix}-{0}-${uniqueString(resourceGroup().id, prefix)}'
-var aoaiApiVersion = '2024-08-01-preview'
-
-resource openai 'Microsoft.CognitiveServices/accounts@2023-10-01-preview' = {
- name: format(uniqueNameFormat, 'openai')
- location: azureOpenAILocation
- tags: tags
- kind: 'OpenAI'
- sku: {
- name: 'S0'
- }
- properties: {
- customSubDomainName: format(uniqueNameFormat, 'openai')
- }
- resource gpt4o 'deployments' = {
- name: 'gpt-4o'
- sku: {
- name: 'GlobalStandard'
- capacity: 15
- }
- properties: {
- model: {
- format: 'OpenAI'
- name: 'gpt-4o'
- version: '2024-08-06'
- }
- versionUpgradeOption: 'NoAutoUpgrade'
- }
- }
-}
-
-resource aoaiUserRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-05-01-preview' existing = {
- name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' //'Cognitive Services OpenAI User'
-}
-
-resource devAoaiRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if(!empty(trim(developerPrincipalId))) {
- name: guid(developerPrincipalId, openai.id, aoaiUserRoleDefinition.id)
- scope: openai
- properties: {
- principalId: developerPrincipalId
- roleDefinitionId: aoaiUserRoleDefinition.id
- principalType: 'User'
- }
-}
-
-resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = {
- name: format(uniqueNameFormat, 'cosmos')
- location: cosmosLocation
- tags: tags
- kind: 'GlobalDocumentDB'
- properties: {
- databaseAccountOfferType: 'Standard'
- enableFreeTier: false
- locations: [
- {
- failoverPriority: 0
- locationName: cosmosLocation
- }
- ]
- capabilities: [ { name: 'EnableServerless' } ]
- }
-
- resource contributorRoleDefinition 'sqlRoleDefinitions' existing = {
- name: '00000000-0000-0000-0000-000000000002'
- }
-
- resource devRoleAssignment 'sqlRoleAssignments' = if(!empty(trim(developerPrincipalId))) {
- name: guid(developerPrincipalId, contributorRoleDefinition.id)
- properties: {
- principalId: developerPrincipalId
- roleDefinitionId: contributorRoleDefinition.id
- scope: cosmos.id
- }
- }
-
- resource autogenDb 'sqlDatabases' = {
- name: 'autogen'
- properties: {
- resource: {
- id: 'autogen'
- createMode: 'Default'
- }
- }
-
- resource memoryContainer 'containers' = {
- name: 'memory'
- properties: {
- resource: {
- id: 'memory'
- partitionKey: {
- kind: 'Hash'
- version: 2
- paths: [
- '/session_id'
- ]
- }
- }
- }
- }
- }
-}
-
-
-
-output COSMOSDB_ENDPOINT string = cosmos.properties.documentEndpoint
-output COSMOSDB_DATABASE string = cosmos::autogenDb.name
-output COSMOSDB_CONTAINER string = cosmos::autogenDb::memoryContainer.name
-output AZURE_OPENAI_ENDPOINT string = openai.properties.endpoint
-output AZURE_OPENAI_DEPLOYMENT_NAME string = openai::gpt4o.name
-output AZURE_OPENAI_API_VERSION string = aoaiApiVersion
-
diff --git a/infra/old/00-older/macae-large.bicepparam b/infra/old/00-older/macae-large.bicepparam
deleted file mode 100644
index 3e88f4452..000000000
--- a/infra/old/00-older/macae-large.bicepparam
+++ /dev/null
@@ -1,11 +0,0 @@
-using './macae.bicep'
-
-param resourceSize = {
- gpt4oCapacity: 50
- containerAppSize: {
- cpu: '2.0'
- memory: '4.0Gi'
- minReplicas: 1
- maxReplicas: 1
- }
-}
diff --git a/infra/old/00-older/macae-mini.bicepparam b/infra/old/00-older/macae-mini.bicepparam
deleted file mode 100644
index ee3d65127..000000000
--- a/infra/old/00-older/macae-mini.bicepparam
+++ /dev/null
@@ -1,11 +0,0 @@
-using './macae.bicep'
-
-param resourceSize = {
- gpt4oCapacity: 15
- containerAppSize: {
- cpu: '1.0'
- memory: '2.0Gi'
- minReplicas: 0
- maxReplicas: 1
- }
-}
diff --git a/infra/old/00-older/macae.bicep b/infra/old/00-older/macae.bicep
deleted file mode 100644
index bfa56c9a1..000000000
--- a/infra/old/00-older/macae.bicep
+++ /dev/null
@@ -1,362 +0,0 @@
-@description('Location for all resources.')
-param location string = resourceGroup().location
-
-@description('location for Cosmos DB resources.')
-// prompt for this as there is often quota restrictions
-param cosmosLocation string
-
-@description('Location for OpenAI resources.')
-// prompt for this as there is often quota restrictions
-param azureOpenAILocation string
-
-@description('A prefix to add to the start of all resource names. Note: A "unique" suffix will also be added')
-param prefix string = 'macae'
-
-@description('Tags to apply to all deployed resources')
-param tags object = {}
-
-@description('The size of the resources to deploy, defaults to a mini size')
-param resourceSize {
- gpt4oCapacity: int
- containerAppSize: {
- cpu: string
- memory: string
- minReplicas: int
- maxReplicas: int
- }
-} = {
- gpt4oCapacity: 50
- containerAppSize: {
- cpu: '2.0'
- memory: '4.0Gi'
- minReplicas: 1
- maxReplicas: 1
- }
-}
-
-
-// var appVersion = 'latest'
-// var resgistryName = 'biabcontainerreg'
-// var dockerRegistryUrl = 'https://${resgistryName}.azurecr.io'
-var placeholderImage = 'hello-world:latest'
-
-var uniqueNameFormat = '${prefix}-{0}-${uniqueString(resourceGroup().id, prefix)}'
-var uniqueShortNameFormat = '${toLower(prefix)}{0}${uniqueString(resourceGroup().id, prefix)}'
-//var aoaiApiVersion = '2024-08-01-preview'
-
-
-resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
- name: format(uniqueNameFormat, 'logs')
- location: location
- tags: tags
- properties: {
- retentionInDays: 30
- sku: {
- name: 'PerGB2018'
- }
- }
-}
-
-resource appInsights 'Microsoft.Insights/components@2020-02-02-preview' = {
- name: format(uniqueNameFormat, 'appins')
- location: location
- kind: 'web'
- properties: {
- Application_Type: 'web'
- WorkspaceResourceId: logAnalytics.id
- }
-}
-
-resource openai 'Microsoft.CognitiveServices/accounts@2023-10-01-preview' = {
- name: format(uniqueNameFormat, 'openai')
- location: azureOpenAILocation
- tags: tags
- kind: 'OpenAI'
- sku: {
- name: 'S0'
- }
- properties: {
- customSubDomainName: format(uniqueNameFormat, 'openai')
- }
- resource gpt4o 'deployments' = {
- name: 'gpt-4o'
- sku: {
- name: 'GlobalStandard'
- capacity: resourceSize.gpt4oCapacity
- }
- properties: {
- model: {
- format: 'OpenAI'
- name: 'gpt-4o'
- version: '2024-08-06'
- }
- versionUpgradeOption: 'NoAutoUpgrade'
- }
- }
-}
-
-resource aoaiUserRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-05-01-preview' existing = {
- name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' //'Cognitive Services OpenAI User'
-}
-
-resource acaAoaiRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(containerApp.id, openai.id, aoaiUserRoleDefinition.id)
- scope: openai
- properties: {
- principalId: containerApp.identity.principalId
- roleDefinitionId: aoaiUserRoleDefinition.id
- principalType: 'ServicePrincipal'
- }
-}
-
-resource acr 'Microsoft.ContainerRegistry/registries@2023-11-01-preview' = {
- name: format(uniqueShortNameFormat, 'acr')
- location: location
- sku: {
- name: 'Standard'
- }
- properties: {
- adminUserEnabled: true // Add this line
- }
-}
-
-resource pullIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = {
- name: format(uniqueNameFormat, 'containerapp-pull')
- location: location
-}
-
-resource acrPullDefinition 'Microsoft.Authorization/roleDefinitions@2022-05-01-preview' existing = {
- name: '7f951dda-4ed3-4680-a7ca-43fe172d538d' //'AcrPull'
-}
-
-resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(acr.id, pullIdentity.id, acrPullDefinition.id)
- properties: {
- principalId: pullIdentity.properties.principalId
- principalType: 'ServicePrincipal'
- roleDefinitionId: acrPullDefinition.id
- }
-}
-
-resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = {
- name: format(uniqueNameFormat, 'cosmos')
- location: cosmosLocation
- tags: tags
- kind: 'GlobalDocumentDB'
- properties: {
- databaseAccountOfferType: 'Standard'
- enableFreeTier: false
- locations: [
- {
- failoverPriority: 0
- locationName: cosmosLocation
- }
- ]
- capabilities: [ { name: 'EnableServerless' } ]
- }
-
- resource contributorRoleDefinition 'sqlRoleDefinitions' existing = {
- name: '00000000-0000-0000-0000-000000000002'
- }
-
- resource autogenDb 'sqlDatabases' = {
- name: 'autogen'
- properties: {
- resource: {
- id: 'autogen'
- createMode: 'Default'
- }
- }
-
- resource memoryContainer 'containers' = {
- name: 'memory'
- properties: {
- resource: {
- id: 'memory'
- partitionKey: {
- kind: 'Hash'
- version: 2
- paths: [
- '/session_id'
- ]
- }
- }
- }
- }
- }
-}
-
-resource containerAppEnv 'Microsoft.App/managedEnvironments@2024-03-01' = {
- name: format(uniqueNameFormat, 'containerapp')
- location: location
- tags: tags
- properties: {
- daprAIConnectionString: appInsights.properties.ConnectionString
- appLogsConfiguration: {
- destination: 'log-analytics'
- logAnalyticsConfiguration: {
- customerId: logAnalytics.properties.customerId
- sharedKey: logAnalytics.listKeys().primarySharedKey
- }
- }
- }
- resource aspireDashboard 'dotNetComponents@2024-02-02-preview' = {
- name: 'aspire-dashboard'
- properties: {
- componentType: 'AspireDashboard'
- }
- }
-}
-
-resource acaCosomsRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2024-05-15' = {
- name: guid(containerApp.id, cosmos::contributorRoleDefinition.id)
- parent: cosmos
- properties: {
- principalId: containerApp.identity.principalId
- roleDefinitionId: cosmos::contributorRoleDefinition.id
- scope: cosmos.id
- }
-}
-
-@description('')
-resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
- name: '${prefix}-backend'
- location: location
- tags: tags
- identity: {
- type: 'SystemAssigned, UserAssigned'
- userAssignedIdentities: {
- '${pullIdentity.id}': {}
- }
- }
- properties: {
- managedEnvironmentId: containerAppEnv.id
- configuration: {
- ingress: {
- targetPort: 8000
- external: true
- corsPolicy: {
- allowedOrigins: [
- 'https://${format(uniqueNameFormat, 'frontend')}.azurewebsites.net'
- 'http://${format(uniqueNameFormat, 'frontend')}.azurewebsites.net'
- ]
- }
- }
- activeRevisionsMode: 'Single'
- }
- template: {
- scale: {
- minReplicas: resourceSize.containerAppSize.minReplicas
- maxReplicas: resourceSize.containerAppSize.maxReplicas
- rules: [
- {
- name: 'http-scaler'
- http: {
- metadata: {
- concurrentRequests: '100'
- }
- }
- }
- ]
- }
- containers: [
- {
- name: 'backend'
- image: placeholderImage
- resources: {
- cpu: json(resourceSize.containerAppSize.cpu)
- memory: resourceSize.containerAppSize.memory
- }
- }
- // env: [
- // {
- // name: 'COSMOSDB_ENDPOINT'
- // value: cosmos.properties.documentEndpoint
- // }
- // {
- // name: 'COSMOSDB_DATABASE'
- // value: cosmos::autogenDb.name
- // }
- // {
- // name: 'COSMOSDB_CONTAINER'
- // value: cosmos::autogenDb::memoryContainer.name
- // }
- // {
- // name: 'AZURE_OPENAI_ENDPOINT'
- // value: openai.properties.endpoint
- // }
- // {
- // name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
- // value: openai::gpt4o.name
- // }
- // {
- // name: 'AZURE_OPENAI_API_VERSION'
- // value: aoaiApiVersion
- // }
- // {
- // name: 'FRONTEND_SITE_NAME'
- // value: 'https://${format(uniqueNameFormat, 'frontend')}.azurewebsites.net'
- // }
- // ]
- // }
- ]
- }
-
- }
-
- }
-resource frontendAppServicePlan 'Microsoft.Web/serverfarms@2021-02-01' = {
- name: format(uniqueNameFormat, 'frontend-plan')
- location: location
- tags: tags
- sku: {
- name: 'P1v2'
- capacity: 1
- tier: 'PremiumV2'
- }
- properties: {
- reserved: true
- }
- kind: 'linux' // Add this line to support Linux containers
-}
-
-resource frontendAppService 'Microsoft.Web/sites@2021-02-01' = {
- name: format(uniqueNameFormat, 'frontend')
- location: location
- tags: tags
- kind: 'app,linux,container' // Add this line
- properties: {
- serverFarmId: frontendAppServicePlan.id
- reserved: true
- siteConfig: {
- linuxFxVersion:''//'DOCKER|${frontendDockerImageURL}'
- appSettings: [
- {
- name: 'DOCKER_REGISTRY_SERVER_URL'
- value: acr.properties.loginServer
- }
- {
- name: 'WEBSITES_PORT'
- value: '3000'
- }
- {
- name: 'WEBSITES_CONTAINER_START_TIME_LIMIT' // Add startup time limit
- value: '1800' // 30 minutes, adjust as needed
- }
- {
- name: 'BACKEND_API_URL'
- value: 'https://${containerApp.properties.configuration.ingress.fqdn}'
- }
- ]
- }
- }
- dependsOn: [containerApp]
- identity: {
- type: 'SystemAssigned, UserAssigned'
- userAssignedIdentities: {
- '${pullIdentity.id}': {}
- }
- }
-}
-
-output cosmosAssignCli string = 'az cosmosdb sql role assignment create --resource-group "${resourceGroup().name}" --account-name "${cosmos.name}" --role-definition-id "${cosmos::contributorRoleDefinition.id}" --scope "${cosmos.id}" --principal-id "fill-in"'
diff --git a/infra/old/00-older/main.bicep b/infra/old/00-older/main.bicep
deleted file mode 100644
index c84added1..000000000
--- a/infra/old/00-older/main.bicep
+++ /dev/null
@@ -1,1298 +0,0 @@
-extension graphV1
-//extension graphBeta
-
-metadata name = ''
-metadata description = ''
-
-@description('Required. The prefix to add in the default names given to all deployed Azure resources.')
-@maxLength(19)
-param solutionPrefix string
-
-@description('Optional. Location for all Resources.')
-param solutionLocation string = resourceGroup().location
-
-@description('Optional. Enable/Disable usage telemetry for module.')
-param enableTelemetry bool
-
-@description('Optional. Enable/Disable usage telemetry for module.')
-param enableNetworkSecurity bool
-
-@description('Optional. The tags to apply to all deployed Azure resources.')
-param tags object = {
- app: solutionPrefix
- location: solutionLocation
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Log Analytics Workspace resource.')
-param logAnalyticsWorkspaceConfiguration logAnalyticsWorkspaceConfigurationType = {
- enabled: true
- name: '${solutionPrefix}laws'
- location: solutionLocation
- sku: 'PerGB2018'
- tags: tags
- dataRetentionInDays: 30
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Application Insights resource.')
-param applicationInsightsConfiguration applicationInsightsConfigurationType = {
- enabled: true
- name: '${solutionPrefix}appi'
- location: solutionLocation
- tags: tags
- retentionInDays: 30
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Managed Identity resource.')
-param userAssignedManagedIdentityConfiguration userAssignedManagedIdentityType = {
- enabled: true
- name: '${solutionPrefix}mgid'
- location: solutionLocation
- tags: tags
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the backend subnet.')
-param networkSecurityGroupBackendConfiguration networkSecurityGroupConfigurationType = {
- enabled: enableNetworkSecurity
- name: '${solutionPrefix}nsgr-backend'
- location: solutionLocation
- tags: tags
- securityRules: [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the containers subnet.')
-param networkSecurityGroupContainersConfiguration networkSecurityGroupConfigurationType = {
- enabled: enableNetworkSecurity
- name: '${solutionPrefix}nsgr-containers'
- location: solutionLocation
- tags: tags
- securityRules: [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the Bastion subnet.')
-param networkSecurityGroupBastionConfiguration networkSecurityGroupConfigurationType = {
- enabled: enableNetworkSecurity
- name: '${solutionPrefix}nsgr-bastion'
- location: solutionLocation
- tags: tags
- securityRules: [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the administration subnet.')
-param networkSecurityGroupAdministrationConfiguration networkSecurityGroupConfigurationType = {
- enabled: enableNetworkSecurity
- name: '${solutionPrefix}nsgr-administration'
- location: solutionLocation
- tags: tags
- securityRules: [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
-}
-
-@description('Optional. Configuration for the virtual machine.')
-param virtualMachineConfiguration virtualMachineConfigurationType = {
- enabled: enableNetworkSecurity
- adminUsername: 'adminuser'
- adminPassword: guid(solutionPrefix, subscription().subscriptionId)
-}
-var virtualMachineEnabled = virtualMachineConfiguration.?enabled ?? true
-
-@description('Optional. Configuration for the virtual machine.')
-param virtualNetworkConfiguration virtualNetworkConfigurationType = {
- enabled: enableNetworkSecurity
-}
-var virtualNetworkEnabled = virtualNetworkConfiguration.?enabled ?? true
-
-@description('Optional. The configuration of the Entra ID Application used to authenticate the website.')
-param entraIdApplicationConfiguration entraIdApplicationConfigurationType = {
- enabled: false
-}
-
-@description('Optional. The UTC time deployment.')
-param deploymentTime string = utcNow()
-
-//
-// Add your parameters here
-//
-
-// ============== //
-// Resources //
-// ============== //
-
-/* #disable-next-line no-deployments-resources
-resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
- name: '46d3xbcp.[[REPLACE WITH TELEMETRY IDENTIFIER]].${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, 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'
- }
- }
- }
- }
-} */
-
-// ========== Log Analytics Workspace ========== //
-// Log Analytics configuration defaults
-var logAnalyticsWorkspaceEnabled = logAnalyticsWorkspaceConfiguration.?enabled ?? true
-var logAnalyticsWorkspaceResourceName = logAnalyticsWorkspaceConfiguration.?name ?? '${solutionPrefix}-laws'
-var logAnalyticsWorkspaceTags = logAnalyticsWorkspaceConfiguration.?tags ?? tags
-var logAnalyticsWorkspaceLocation = logAnalyticsWorkspaceConfiguration.?location ?? solutionLocation
-var logAnalyticsWorkspaceSkuName = logAnalyticsWorkspaceConfiguration.?sku ?? 'PerGB2018'
-var logAnalyticsWorkspaceDataRetentionInDays = logAnalyticsWorkspaceConfiguration.?dataRetentionInDays ?? 30
-module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.2' = if (logAnalyticsWorkspaceEnabled) {
- name: take('operational-insights.workspace.${logAnalyticsWorkspaceResourceName}', 64)
- params: {
- name: logAnalyticsWorkspaceResourceName
- tags: logAnalyticsWorkspaceTags
- location: logAnalyticsWorkspaceLocation
- enableTelemetry: enableTelemetry
- skuName: logAnalyticsWorkspaceSkuName
- dataRetention: logAnalyticsWorkspaceDataRetentionInDays
- diagnosticSettings: [{ useThisWorkspace: true }]
- }
-}
-
-// ========== Application Insights ========== //
-// Application Insights configuration defaults
-var applicationInsightsEnabled = applicationInsightsConfiguration.?enabled ?? true
-var applicationInsightsResourceName = applicationInsightsConfiguration.?name ?? '${solutionPrefix}appi'
-var applicationInsightsTags = applicationInsightsConfiguration.?tags ?? tags
-var applicationInsightsLocation = applicationInsightsConfiguration.?location ?? solutionLocation
-var applicationInsightsRetentionInDays = applicationInsightsConfiguration.?retentionInDays ?? 365
-module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (applicationInsightsEnabled) {
- name: take('insights.component.${applicationInsightsResourceName}', 64)
- params: {
- name: applicationInsightsResourceName
- workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
- location: applicationInsightsLocation
- enableTelemetry: enableTelemetry
- tags: applicationInsightsTags
- retentionInDays: applicationInsightsRetentionInDays
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- kind: 'web'
- disableIpMasking: false
- flowType: 'Bluefield'
- }
-}
-
-// ========== User assigned identity Web App ========== //
-var userAssignedManagedIdentityEnabled = userAssignedManagedIdentityConfiguration.?enabled ?? true
-var userAssignedManagedIdentityResourceName = userAssignedManagedIdentityConfiguration.?name ?? '${solutionPrefix}uaid'
-var userAssignedManagedIdentityTags = userAssignedManagedIdentityConfiguration.?tags ?? tags
-var userAssignedManagedIdentityLocation = userAssignedManagedIdentityConfiguration.?location ?? solutionLocation
-module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = if (userAssignedManagedIdentityEnabled) {
- name: take('managed-identity.user-assigned-identity.${userAssignedManagedIdentityResourceName}', 64)
- params: {
- name: userAssignedManagedIdentityResourceName
- tags: userAssignedManagedIdentityTags
- location: userAssignedManagedIdentityLocation
- enableTelemetry: enableTelemetry
- }
-}
-
-// ========== Network Security Groups ========== //
-var networkSecurityGroupBackendEnabled = networkSecurityGroupBackendConfiguration.?enabled ?? true
-var networkSecurityGroupBackendResourceName = networkSecurityGroupBackendConfiguration.?name ?? '${solutionPrefix}nsgr-backend'
-var networkSecurityGroupBackendTags = networkSecurityGroupBackendConfiguration.?tags ?? tags
-var networkSecurityGroupBackendLocation = networkSecurityGroupBackendConfiguration.?location ?? solutionLocation
-var networkSecurityGroupBackendSecurityRules = networkSecurityGroupBackendConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
-]
-module networkSecurityGroupBackend 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupBackendEnabled) {
- name: take('network.network-security-group.${networkSecurityGroupBackendResourceName}', 64)
- params: {
- name: networkSecurityGroupBackendResourceName
- location: networkSecurityGroupBackendLocation
- tags: networkSecurityGroupBackendTags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- securityRules: networkSecurityGroupBackendSecurityRules
- }
-}
-
-var networkSecurityGroupContainersEnabled = networkSecurityGroupContainersConfiguration.?enabled ?? true
-var networkSecurityGroupContainersResourceName = networkSecurityGroupContainersConfiguration.?name ?? '${solutionPrefix}nsgr-containers'
-var networkSecurityGroupContainersTags = networkSecurityGroupContainersConfiguration.?tags ?? tags
-var networkSecurityGroupContainersLocation = networkSecurityGroupContainersConfiguration.?location ?? solutionLocation
-var networkSecurityGroupContainersSecurityRules = networkSecurityGroupContainersConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
-]
-module networkSecurityGroupContainers 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupContainersEnabled) {
- name: take('network.network-security-group.${networkSecurityGroupContainersResourceName}', 64)
- params: {
- name: networkSecurityGroupContainersResourceName
- location: networkSecurityGroupContainersLocation
- tags: networkSecurityGroupContainersTags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- securityRules: networkSecurityGroupContainersSecurityRules
- }
-}
-
-var networkSecurityGroupBastionEnabled = networkSecurityGroupBastionConfiguration.?enabled ?? true
-var networkSecurityGroupBastionResourceName = networkSecurityGroupBastionConfiguration.?name ?? '${solutionPrefix}nsgr-bastion'
-var networkSecurityGroupBastionTags = networkSecurityGroupBastionConfiguration.?tags ?? tags
-var networkSecurityGroupBastionLocation = networkSecurityGroupBastionConfiguration.?location ?? solutionLocation
-var networkSecurityGroupBastionSecurityRules = networkSecurityGroupBastionConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
-]
-module networkSecurityGroupBastion 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupBastionEnabled) {
- name: take('network.network-security-group.${networkSecurityGroupBastionResourceName}', 64)
- params: {
- name: networkSecurityGroupBastionResourceName
- location: networkSecurityGroupBastionLocation
- tags: networkSecurityGroupBastionTags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- securityRules: networkSecurityGroupBastionSecurityRules
- }
-}
-
-var networkSecurityGroupAdministrationEnabled = networkSecurityGroupAdministrationConfiguration.?enabled ?? true
-var networkSecurityGroupAdministrationResourceName = networkSecurityGroupAdministrationConfiguration.?name ?? '${solutionPrefix}nsgr-administration'
-var networkSecurityGroupAdministrationTags = networkSecurityGroupAdministrationConfiguration.?tags ?? tags
-var networkSecurityGroupAdministrationLocation = networkSecurityGroupAdministrationConfiguration.?location ?? solutionLocation
-var networkSecurityGroupAdministrationSecurityRules = networkSecurityGroupAdministrationConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
-]
-module networkSecurityGroupAdministration 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupAdministrationEnabled) {
- name: take('network.network-security-group.${networkSecurityGroupAdministrationResourceName}', 64)
- params: {
- name: networkSecurityGroupAdministrationResourceName
- location: networkSecurityGroupAdministrationLocation
- tags: networkSecurityGroupAdministrationTags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- securityRules: networkSecurityGroupAdministrationSecurityRules
- }
-}
-
-// ========== Virtual Network ========== //
-
-module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = if (virtualNetworkEnabled) {
- name: 'network-virtual-network'
- params: {
- name: '${solutionPrefix}vnet'
- location: solutionLocation
- tags: tags
- enableTelemetry: enableTelemetry
- addressPrefixes: ['10.0.0.0/8']
- subnets: [
- // The default subnet **must** be the first in the subnets array
- {
- name: 'backend'
- addressPrefix: '10.0.0.0/27'
- //defaultOutboundAccess: false TODO: check this configuration for a more restricted outbound access
- networkSecurityGroupResourceId: networkSecurityGroupBackend.outputs.resourceId
- }
- {
- name: 'administration'
- addressPrefix: '10.0.0.32/27'
- networkSecurityGroupResourceId: networkSecurityGroupAdministration.outputs.resourceId
- //defaultOutboundAccess: false TODO: check this configuration for a more restricted outbound access
- //natGatewayResourceId: natGateway.outputs.resourceId
- }
- {
- // For Azure Bastion resources deployed on or after November 2, 2021, the minimum AzureBastionSubnet size is /26 or larger (/25, /24, etc.).
- // https://learn.microsoft.com/en-us/azure/bastion/configuration-settings#subnet
- name: 'AzureBastionSubnet' //This exact name is required for Azure Bastion
- addressPrefix: '10.0.0.64/26'
- networkSecurityGroupResourceId: networkSecurityGroupBastion.outputs.resourceId
- }
- {
- // If you use your own VNet, you need to provide a subnet that is dedicated exclusively to the Container App environment you deploy. This subnet isn't available to other services
- // https://learn.microsoft.com/en-us/azure/container-apps/networking?tabs=workload-profiles-env%2Cazure-cli#custom-vnet-configuration
- name: 'containers'
- addressPrefix: '10.0.1.0/23' //subnet of size /23 is required for container app
- //defaultOutboundAccess: false TODO: check this configuration for a more restricted outbound access
- delegation: 'Microsoft.App/environments'
- networkSecurityGroupResourceId: networkSecurityGroupContainers.outputs.resourceId
- privateEndpointNetworkPolicies: 'Disabled'
- privateLinkServiceNetworkPolicies: 'Enabled'
- }
- ]
- }
-}
-
-// ========== Bastion host ========== //
-
-module bastionHost 'br/public:avm/res/network/bastion-host:0.6.1' = if (virtualNetworkEnabled) {
- name: 'network-dns-zone-bastion-host'
- params: {
- name: '${solutionPrefix}bstn'
- location: solutionLocation
- skuName: 'Standard'
- enableTelemetry: enableTelemetry
- tags: tags
- virtualNetworkResourceId: virtualNetwork.outputs.resourceId
- publicIPAddressObject: {
- name: '${solutionPrefix}pbipbstn'
- }
- disableCopyPaste: false
- enableFileCopy: false
- enableIpConnect: true
- //enableKerberos: bastionConfiguration.?enableKerberos
- enableShareableLink: true
- //scaleUnits: bastionConfiguration.?scaleUnits
- }
-}
-
-// ========== Virtual machine ========== //
-
-module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.13.0' = if (virtualNetworkEnabled && virtualMachineEnabled) {
- name: 'compute-virtual-machine'
- params: {
- name: '${solutionPrefix}vmws'
- computerName: take('${solutionPrefix}vmws', 15)
- location: solutionLocation
- tags: tags
- enableTelemetry: enableTelemetry
- adminUsername: virtualMachineConfiguration.?adminUsername!
- adminPassword: virtualMachineConfiguration.?adminPassword!
- nicConfigurations: [
- {
- //networkSecurityGroupResourceId: virtualMachineConfiguration.?nicConfigurationConfiguration.networkSecurityGroupResourceId
- nicSuffix: 'nic01'
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- ipConfigurations: [
- {
- name: 'ipconfig01'
- subnetResourceId: virtualNetwork.outputs.subnetResourceIds[1]
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- }
- ]
- }
- ]
- imageReference: {
- publisher: 'microsoft-dsvm'
- offer: 'dsvm-win-2022'
- sku: 'winserver-2022'
- version: 'latest'
- }
- osDisk: {
- createOption: 'FromImage'
- managedDisk: {
- storageAccountType: 'Premium_ZRS'
- }
- diskSizeGB: 128
- caching: 'ReadWrite'
- }
- //patchMode: virtualMachineConfiguration.?patchMode
- osType: 'Windows'
- encryptionAtHost: false //The property 'securityProfile.encryptionAtHost' is not valid because the 'Microsoft.Compute/EncryptionAtHost' feature is not enabled for this subscription.
- vmSize: 'Standard_D2s_v3'
- zone: 0
- extensionAadJoinConfig: {
- enabled: true
- typeHandlerVersion: '1.0'
- }
- // extensionMonitoringAgentConfig: {
- // enabled: true
- // }
- // maintenanceConfigurationResourceId: virtualMachineConfiguration.?maintenanceConfigurationResourceId
- }
-}
-// ========== DNS Zone for AI Foundry: Open AI ========== //
-var openAiSubResource = 'account'
-var openAiPrivateDnsZones = {
- 'privatelink.cognitiveservices.azure.com': openAiSubResource
- 'privatelink.openai.azure.com': openAiSubResource
- 'privatelink.services.ai.azure.com': openAiSubResource
-}
-
-module privateDnsZonesAiServices 'br/public:avm/res/network/private-dns-zone:0.7.1' = [
- for zone in objectKeys(openAiPrivateDnsZones): if (virtualNetworkEnabled) {
- name: 'network-dns-zone-${uniqueString(deployment().name, zone)}'
- params: {
- name: zone
- tags: tags
- enableTelemetry: enableTelemetry
- virtualNetworkLinks: [{ virtualNetworkResourceId: virtualNetwork.outputs.resourceId }]
- }
- }
-]
-
-// ========== AI Foundry: AI Services ==========
-// NOTE: Required version 'Microsoft.CognitiveServices/accounts@2024-04-01-preview' not available in AVM
-var aiFoundryAiServicesModelDeployment = {
- format: 'OpenAI'
- name: 'gpt-4o'
- version: '2024-08-06'
- sku: {
- name: 'GlobalStandard'
- capacity: 50
- }
- raiPolicyName: 'Microsoft.Default'
-}
-
-var aiFoundryAiServicesAccountName = '${solutionPrefix}aifdaisv'
-module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.10.2' = {
- name: 'cognitive-services-account'
- params: {
- name: aiFoundryAiServicesAccountName
- tags: tags
- location: solutionLocation
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- sku: 'S0'
- kind: 'AIServices'
- disableLocalAuth: false //Should be set to true for WAF aligned configuration
- customSubDomainName: aiFoundryAiServicesAccountName
- apiProperties: {
- //staticsEnabled: false
- }
- //publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled'
- publicNetworkAccess: 'Enabled' //TODO: connection via private endpoint is not working from containers network. Change this when fixed
- privateEndpoints: virtualNetworkEnabled
- ? ([
- {
- subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0]
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: map(objectKeys(openAiPrivateDnsZones), zone => {
- name: replace(zone, '.', '-')
- privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone)
- })
- }
- }
- ])
- : []
- roleAssignments: [
- // {
- // principalId: userAssignedIdentity.outputs.principalId
- // principalType: 'ServicePrincipal'
- // roleDefinitionIdOrName: 'Cognitive Services OpenAI User'
- // }
- {
- principalId: containerApp.outputs.?systemAssignedMIPrincipalId!
- principalType: 'ServicePrincipal'
- roleDefinitionIdOrName: 'Cognitive Services OpenAI User'
- }
- ]
- deployments: [
- {
- name: aiFoundryAiServicesModelDeployment.name
- model: {
- format: aiFoundryAiServicesModelDeployment.format
- name: aiFoundryAiServicesModelDeployment.name
- version: aiFoundryAiServicesModelDeployment.version
- }
- raiPolicyName: aiFoundryAiServicesModelDeployment.raiPolicyName
- sku: {
- name: aiFoundryAiServicesModelDeployment.sku.name
- capacity: aiFoundryAiServicesModelDeployment.sku.capacity
- }
- }
- ]
- }
-}
-
-// AI Foundry: storage account
-
-var storageAccountPrivateDnsZones = {
- 'privatelink.blob.${environment().suffixes.storage}': 'blob'
- 'privatelink.file.${environment().suffixes.storage}': 'file'
-}
-
-module privateDnsZonesAiFoundryStorageAccount 'br/public:avm/res/network/private-dns-zone:0.3.1' = [
- for zone in objectKeys(storageAccountPrivateDnsZones): if (virtualNetworkEnabled) {
- name: 'network-dns-zone-aifd-stac-${zone}'
- params: {
- name: zone
- tags: tags
- enableTelemetry: enableTelemetry
- virtualNetworkLinks: [
- {
- virtualNetworkResourceId: virtualNetwork.outputs.resourceId
- }
- ]
- }
- }
-]
-
-var aiFoundryStorageAccountName = '${solutionPrefix}aifdstrg'
-module aiFoundryStorageAccount 'br/public:avm/res/storage/storage-account:0.18.2' = {
- name: 'storage-storage-account'
- dependsOn: [
- privateDnsZonesAiFoundryStorageAccount
- ]
- params: {
- name: aiFoundryStorageAccountName
- location: solutionLocation
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- skuName: 'Standard_LRS'
- allowSharedKeyAccess: false
- networkAcls: {
- bypass: 'AzureServices'
- defaultAction: 'Allow'
- }
- blobServices: {
- deleteRetentionPolicyEnabled: false
- containerDeleteRetentionPolicyDays: 7
- containerDeleteRetentionPolicyEnabled: false
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- }
- publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled'
- allowBlobPublicAccess: virtualNetworkEnabled ? false : true
- privateEndpoints: virtualNetworkEnabled
- ? map(items(storageAccountPrivateDnsZones), zone => {
- name: 'pep-${zone.value}-${aiFoundryStorageAccountName}'
- customNetworkInterfaceName: 'nic-${zone.value}-${aiFoundryStorageAccountName}'
- service: zone.value
- subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] ?? ''
- privateDnsZoneResourceIds: [resourceId('Microsoft.Network/privateDnsZones', zone.key)]
- })
- : null
- roleAssignments: [
- {
- principalId: userAssignedIdentity.outputs.principalId
- roleDefinitionIdOrName: 'Storage Blob Data Contributor'
- principalType: 'ServicePrincipal'
- }
- ]
- }
-}
-
-// AI Foundry: AI Hub
-var mlTargetSubResource = 'amlworkspace'
-var mlPrivateDnsZones = {
- 'privatelink.api.azureml.ms': mlTargetSubResource
- 'privatelink.notebooks.azure.net': mlTargetSubResource
-}
-module privateDnsZonesAiFoundryWorkspaceHub 'br/public:avm/res/network/private-dns-zone:0.3.1' = [
- for zone in objectKeys(mlPrivateDnsZones): if (virtualNetworkEnabled) {
- name: 'network-dns-zone-${zone}'
- params: {
- name: zone
- enableTelemetry: enableTelemetry
- tags: tags
- virtualNetworkLinks: [
- {
- virtualNetworkResourceId: virtualNetwork.outputs.resourceId
- }
- ]
- }
- }
-]
-var aiFoundryAiHubName = '${solutionPrefix}aifdaihb'
-module aiFoundryAiHub 'modules/ai-hub.bicep' = {
- name: 'modules-ai-hub'
- dependsOn: [
- privateDnsZonesAiFoundryWorkspaceHub
- ]
- params: {
- name: aiFoundryAiHubName
- location: solutionLocation
- tags: tags
- aiFoundryAiServicesName: aiFoundryAiServices.outputs.name
- applicationInsightsResourceId: applicationInsights.outputs.resourceId
- enableTelemetry: enableTelemetry
- logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
- storageAccountResourceId: aiFoundryStorageAccount.outputs.resourceId
- virtualNetworkEnabled: virtualNetworkEnabled
- privateEndpoints: virtualNetworkEnabled
- ? [
- {
- name: 'pep-${mlTargetSubResource}-${aiFoundryAiHubName}'
- customNetworkInterfaceName: 'nic-${mlTargetSubResource}-${aiFoundryAiHubName}'
- service: mlTargetSubResource
- subnetResourceId: virtualNetworkEnabled ? virtualNetwork.?outputs.?subnetResourceIds[0] : null
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: map(objectKeys(mlPrivateDnsZones), zone => {
- name: replace(zone, '.', '-')
- privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone)
- })
- }
- }
- ]
- : []
- }
-}
-
-// AI Foundry: AI Project
-var aiFoundryAiProjectName = '${solutionPrefix}aifdaipj'
-
-module aiFoundryAiProject 'br/public:avm/res/machine-learning-services/workspace:0.12.0' = {
- name: 'machine-learning-services-workspace-project'
- params: {
- name: aiFoundryAiProjectName
- location: solutionLocation
- tags: tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- sku: 'Basic'
- kind: 'Project'
- hubResourceId: aiFoundryAiHub.outputs.resourceId
- roleAssignments: [
- {
- principalId: containerApp.outputs.?systemAssignedMIPrincipalId!
- // Assigning the role with the role name instead of the role ID freezes the deployment at this point
- roleDefinitionIdOrName: '64702f94-c441-49e6-a78b-ef80e0188fee' //'Azure AI Developer'
- principalType: 'ServicePrincipal'
- }
- ]
- }
-}
-
-// ========== DNS Zone for Cosmos DB ========== //
-module privateDnsZonesCosmosDb 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (virtualNetworkEnabled) {
- name: 'network-dns-zone-cosmos-db'
- params: {
- name: 'privatelink.documents.azure.com'
- enableTelemetry: enableTelemetry
- virtualNetworkLinks: [{ virtualNetworkResourceId: virtualNetwork.outputs.resourceId }]
- tags: tags
- }
-}
-
-// ========== Cosmos DB ========== //
-var cosmosDbName = '${solutionPrefix}csdb'
-var cosmosDbDatabaseName = 'autogen'
-var cosmosDbDatabaseMemoryContainerName = 'memory'
-module cosmosDb 'br/public:avm/res/document-db/database-account:0.12.0' = {
- name: 'cosmos-db'
- params: {
- // Required parameters
- name: cosmosDbName
- tags: tags
- location: solutionLocation
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- databaseAccountOfferType: 'Standard'
- enableFreeTier: false
- networkRestrictions: {
- networkAclBypass: 'None'
- publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled'
- }
- privateEndpoints: virtualNetworkEnabled
- ? [
- {
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [{ privateDnsZoneResourceId: privateDnsZonesCosmosDb.outputs.resourceId }]
- }
- service: 'Sql'
- subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0]
- }
- ]
- : []
- sqlDatabases: [
- {
- name: cosmosDbDatabaseName
- containers: [
- {
- name: cosmosDbDatabaseMemoryContainerName
- paths: [
- '/session_id'
- ]
- kind: 'Hash'
- version: 2
- }
- ]
- }
- ]
- locations: [
- {
- locationName: solutionLocation
- failoverPriority: 0
- }
- ]
- capabilitiesToAdd: [
- 'EnableServerless'
- ]
- sqlRoleAssignmentsPrincipalIds: [
- //userAssignedIdentity.outputs.principalId
- containerApp.outputs.?systemAssignedMIPrincipalId
- ]
- sqlRoleDefinitions: [
- {
- // Replace this with built-in role definition Cosmos DB Built-in Data Contributor: https://docs.azure.cn/en-us/cosmos-db/nosql/security/reference-data-plane-roles#cosmos-db-built-in-data-contributor
- roleType: 'CustomRole'
- roleName: 'Cosmos DB SQL Data Contributor'
- name: 'cosmos-db-sql-data-contributor'
- dataAction: [
- 'Microsoft.DocumentDB/databaseAccounts/readMetadata'
- 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*'
- 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
- ]
- }
- ]
- }
-}
-
-// ========== Backend Container App Environment ========== //
-
-module containerAppEnvironment 'modules/container-app-environment.bicep' = {
- name: 'modules-container-app-environment'
- params: {
- name: '${solutionPrefix}cenv'
- tags: tags
- location: solutionLocation
- logAnalyticsResourceName: logAnalyticsWorkspace.outputs.name
- publicNetworkAccess: 'Enabled'
- zoneRedundant: virtualNetworkEnabled ? true : false
- aspireDashboardEnabled: !virtualNetworkEnabled
- vnetConfiguration: virtualNetworkEnabled
- ? {
- internal: false
- infrastructureSubnetId: virtualNetwork.?outputs.?subnetResourceIds[2] ?? ''
- }
- : {}
- }
-}
-
-// module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.0' = {
-// name: 'container-app-environment'
-// params: {
-// name: '${solutionPrefix}cenv'
-// location: solutionLocation
-// tags: tags
-// enableTelemetry: enableTelemetry
-// //daprAIConnectionString: applicationInsights.outputs.connectionString //Troubleshoot: ContainerAppsConfiguration.DaprAIConnectionString is invalid. DaprAIConnectionString can not be set when AppInsightsConfiguration has been set, please set DaprAIConnectionString to null. (Code:InvalidRequestParameterWithDetails
-// appLogsConfiguration: {
-// destination: 'log-analytics'
-// logAnalyticsConfiguration: {
-// customerId: logAnalyticsWorkspace.outputs.logAnalyticsWorkspaceId
-// sharedKey: listKeys(
-// '${resourceGroup().id}/providers/Microsoft.OperationalInsights/workspaces/${logAnalyticsWorkspaceName}',
-// '2023-09-01'
-// ).primarySharedKey
-// }
-// }
-// appInsightsConnectionString: applicationInsights.outputs.connectionString
-// publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled' //TODO: use Azure Front Door WAF or Application Gateway WAF instead
-// zoneRedundant: true //TODO: make it zone redundant for waf aligned
-// infrastructureSubnetResourceId: virtualNetworkEnabled
-// ? virtualNetwork.outputs.subnetResourceIds[1]
-// : null
-// internal: false
-// }
-// }
-
-// ========== Backend Container App Service ========== //
-module containerApp 'br/public:avm/res/app/container-app:0.14.2' = {
- name: 'container-app'
- params: {
- name: '${solutionPrefix}capp'
- tags: tags
- location: solutionLocation
- enableTelemetry: enableTelemetry
- //environmentResourceId: containerAppEnvironment.outputs.resourceId
- environmentResourceId: containerAppEnvironment.outputs.resourceId
- managedIdentities: {
- systemAssigned: true //Replace with user assigned identity
- userAssignedResourceIds: [userAssignedIdentity.outputs.resourceId]
- }
- ingressTargetPort: 8000
- ingressExternal: true
- activeRevisionsMode: 'Single'
- corsPolicy: {
- allowedOrigins: [
- 'https://${webSiteName}.azurewebsites.net'
- 'http://${webSiteName}.azurewebsites.net'
- ]
- }
- scaleSettings: {
- //TODO: Make maxReplicas and minReplicas parameterized
- maxReplicas: 1
- minReplicas: 1
- rules: [
- {
- name: 'http-scaler'
- http: {
- metadata: {
- concurrentRequests: '100'
- }
- }
- }
- ]
- }
- containers: [
- {
- name: 'backend'
- //TODO: Make image parameterized for the registry name and the appversion
- image: 'biabcontainerreg.azurecr.io/macaebackend:fnd01'
- resources: {
- //TODO: Make cpu and memory parameterized
- cpu: '2.0'
- memory: '4.0Gi'
- }
- env: [
- {
- name: 'COSMOSDB_ENDPOINT'
- value: 'https://${cosmosDbName}.documents.azure.com:443/'
- }
- {
- name: 'COSMOSDB_DATABASE'
- value: cosmosDbDatabaseName
- }
- {
- name: 'COSMOSDB_CONTAINER'
- value: cosmosDbDatabaseMemoryContainerName
- }
- {
- name: 'AZURE_OPENAI_ENDPOINT'
- value: 'https://${aiFoundryAiServicesAccountName}.openai.azure.com/'
- }
- {
- name: 'AZURE_OPENAI_MODEL_NAME'
- value: aiFoundryAiServicesModelDeployment.name
- }
- {
- name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
- value: aiFoundryAiServicesModelDeployment.name
- }
- {
- name: 'AZURE_OPENAI_API_VERSION'
- value: '2025-01-01-preview' //TODO: set parameter/variable
- }
- {
- name: 'APPLICATIONINSIGHTS_INSTRUMENTATION_KEY'
- value: applicationInsights.outputs.instrumentationKey
- }
- {
- name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
- value: applicationInsights.outputs.connectionString
- }
- {
- name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING'
- value: '${toLower(replace(solutionLocation,' ',''))}.api.azureml.ms;${subscription().subscriptionId};${resourceGroup().name};${aiFoundryAiProjectName}'
- //Location should be the AI Foundry AI Project location
- }
- {
- name: 'AZURE_AI_SUBSCRIPTION_ID'
- value: subscription().subscriptionId
- }
- {
- name: 'AZURE_AI_RESOURCE_GROUP'
- value: resourceGroup().name
- }
- {
- name: 'AZURE_AI_PROJECT_NAME'
- value: aiFoundryAiProjectName
- }
- {
- name: 'FRONTEND_SITE_NAME'
- value: 'https://${webSiteName}.azurewebsites.net'
- }
- ]
- }
- ]
- }
-}
-
-// ========== Frontend server farm ========== //
-module webServerfarm 'br/public:avm/res/web/serverfarm:0.4.1' = {
- name: 'web-server-farm'
- params: {
- tags: tags
- location: solutionLocation
- name: '${solutionPrefix}sfrm'
- skuName: 'P1v2'
- skuCapacity: 1
- reserved: true
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }]
- kind: 'linux'
- zoneRedundant: false //TODO: make it zone redundant for waf aligned
- }
-}
-
-// ========== Entra ID Application ========== //
-resource entraIdApplication 'Microsoft.Graph/applications@v1.0' = if (entraIdApplicationConfiguration.?enabled!) {
- displayName: '${webSiteName}-app'
- uniqueName: '${webSiteName}-app-${uniqueString(resourceGroup().id, webSiteName)}'
- description: 'EntraId Application for ${webSiteName} authentication'
- passwordCredentials: [
- {
- displayName: 'Credential for website ${webSiteName}'
- endDateTime: dateTimeAdd(deploymentTime, 'P180D')
- // keyId: 'string'
- // startDateTime: 'string'
- }
- ]
-}
-
-var graphAppId = '00000003-0000-0000-c000-000000000000' //Microsoft Graph ID
-// Get the Microsoft Graph service principal so that the scope names can be looked up and mapped to a permission ID
-resource msGraphSP 'Microsoft.Graph/servicePrincipals@v1.0' existing = {
- appId: graphAppId
-}
-
-// ========== Entra ID Service Principal ========== //
-resource entraIdServicePrincipal 'Microsoft.Graph/servicePrincipals@v1.0' = if (entraIdApplicationConfiguration.?enabled!) {
- appId: entraIdApplication.appId
-}
-
-// Grant the OAuth2.0 scopes (requested in parameters) to the basic app, for all users in the tenant
-resource graphScopesAssignment 'Microsoft.Graph/oauth2PermissionGrants@v1.0' = if (entraIdApplicationConfiguration.?enabled!) {
- clientId: entraIdServicePrincipal.id
- resourceId: msGraphSP.id
- consentType: 'AllPrincipals'
- scope: 'User.Read'
-}
-
-// ========== Frontend web site ========== //
-var webSiteName = '${solutionPrefix}wapp'
-var entraIdApplicationCredentialSecretSettingName = 'MICROSOFT_PROVIDER_AUTHENTICATION_SECRET'
-module webSite 'br/public:avm/res/web/site:0.15.1' = {
- name: 'web-site'
- params: {
- tags: tags
- kind: 'app,linux,container'
- name: webSiteName
- location: solutionLocation
- serverFarmResourceId: webServerfarm.outputs.resourceId
- appInsightResourceId: applicationInsights.outputs.resourceId
- siteConfig: {
- linuxFxVersion: 'DOCKER|biabcontainerreg.azurecr.io/macaefrontend:fnd01'
- }
- publicNetworkAccess: 'Enabled' //TODO: use Azure Front Door WAF or Application Gateway WAF instead
- //privateEndpoints: [{ subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] }]
- //Not required, this resource only serves a static website
- appSettingsKeyValuePairs: union(
- {
- SCM_DO_BUILD_DURING_DEPLOYMENT: 'true'
- DOCKER_REGISTRY_SERVER_URL: 'https://biabcontainerreg.azurecr.io'
- WEBSITES_PORT: '3000'
- WEBSITES_CONTAINER_START_TIME_LIMIT: '1800' // 30 minutes, adjust as needed
- BACKEND_API_URL: 'https://${containerApp.outputs.fqdn}'
- AUTH_ENABLED: 'false'
- },
- (entraIdApplicationConfiguration.?enabled!
- ? { '${entraIdApplicationCredentialSecretSettingName}': entraIdApplication.passwordCredentials[0].secretText }
- : {})
- )
- authSettingV2Configuration: {
- platform: {
- enabled: entraIdApplicationConfiguration.?enabled!
- runtimeVersion: '~1'
- }
- login: {
- cookieExpiration: {
- convention: 'FixedTime'
- timeToExpiration: '08:00:00'
- }
- nonce: {
- nonceExpirationInterval: '00:05:00'
- validateNonce: true
- }
- preserveUrlFragmentsForLogins: false
- routes: {}
- tokenStore: {
- azureBlobStorage: {}
- enabled: true
- fileSystem: {}
- tokenRefreshExtensionHours: 72
- }
- }
- globalValidation: {
- requireAuthentication: true
- unauthenticatedClientAction: 'RedirectToLoginPage'
- redirectToProvider: 'azureactivedirectory'
- }
- httpSettings: {
- forwardProxy: {
- convention: 'NoProxy'
- }
- requireHttps: true
- routes: {
- apiPrefix: '/.auth'
- }
- }
- identityProviders: {
- azureActiveDirectory: entraIdApplicationConfiguration.?enabled!
- ? {
- isAutoProvisioned: true
- enabled: true
- login: {
- disableWWWAuthenticate: false
- }
- registration: {
- clientId: entraIdApplication.appId //create application in AAD
- clientSecretSettingName: entraIdApplicationCredentialSecretSettingName
- openIdIssuer: 'https://sts.windows.net/${tenant().tenantId}/v2.0/'
- }
- validation: {
- allowedAudiences: [
- 'api://${entraIdApplication.appId}'
- ]
- defaultAuthorizationPolicy: {
- allowedPrincipals: {}
- allowedApplications: ['86e2d249-6832-461f-8888-cfa0394a5f8c']
- }
- jwtClaimChecks: {}
- }
- }
- : {}
- }
- }
- }
-}
-
-// ============ //
-// Outputs //
-// ============ //
-
-// Add your outputs here
-
-// @description('The resource ID of the resource.')
-// output resourceId string = .id
-
-// @description('The name of the resource.')
-// output name string = .name
-
-// @description('The location the resource was deployed into.')
-// output location string = .location
-
-// ================ //
-// Definitions //
-// ================ //
-//
-// Add your User-defined-types here, if any
-//
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Log Analytics Workspace resource configuration.')
-type logAnalyticsWorkspaceConfigurationType = {
- @description('Optional. If the Log Analytics Workspace resource should be enabled or not.')
- enabled: bool?
-
- @description('Optional. The name of the Log Analytics Workspace resource.')
- @maxLength(63)
- name: string?
-
- @description('Optional. Location for the Log Analytics Workspace resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to for the Log Analytics Workspace resource.')
- tags: object?
-
- @description('Optional. The SKU for the Log Analytics Workspace resource.')
- sku: ('CapacityReservation' | 'Free' | 'LACluster' | 'PerGB2018' | 'PerNode' | 'Premium' | 'Standalone' | 'Standard')?
-
- @description('Optional. The number of days to retain the data in the Log Analytics Workspace. If empty, it will be set to 30 days.')
- @maxValue(730)
- dataRetentionInDays: int?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Application Insights resource configuration.')
-type applicationInsightsConfigurationType = {
- @description('Optional. If the Application Insights resource should be enabled or not.')
- enabled: bool?
-
- @description('Optional. The name of the Application Insights resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Application Insights resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Application Insights resource.')
- tags: object?
-
- @description('Optional. The retention of Application Insights data in days. If empty, Standard will be used.')
- retentionInDays: (120 | 180 | 270 | 30 | 365 | 550 | 60 | 730 | 90)?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Application User Assigned Managed Identity resource configuration.')
-type userAssignedManagedIdentityType = {
- @description('Optional. If the User Assigned Managed Identity resource should be enabled or not.')
- enabled: bool?
-
- @description('Optional. The name of the User Assigned Managed Identity resource.')
- @maxLength(128)
- name: string?
-
- @description('Optional. Location for the User Assigned Managed Identity resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the User Assigned Managed Identity resource.')
- tags: object?
-}
-
-@export()
-import { securityRuleType } from 'br/public:avm/res/network/network-security-group:0.5.1'
-@description('The type for the Multi-Agent Custom Automation Engine Network Security Group resource configuration.')
-type networkSecurityGroupConfigurationType = {
- @description('Optional. If the Network Security Group resource should be enabled or not.')
- enabled: bool?
-
- @description('Optional. The name of the Network Security Group resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Network Security Group resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Network Security Group resource.')
- tags: object?
-
- @description('Optional. The security rules to set for the Network Security Group resource.')
- securityRules: securityRuleType[]?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation virtual machine resource configuration.')
-type virtualMachineConfigurationType = {
- @description('Optional. If the Virtual Machine resource should be enabled or not.')
- enabled: bool?
-
- @description('Required. The username for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module.')
- adminUsername: string?
-
- @description('Required. The password for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module.')
- @secure()
- adminPassword: string?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation virtual network resource configuration.')
-type virtualNetworkConfigurationType = {
- @description('Optional. If the Virtual Network resource should be enabled or not.')
- enabled: bool?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Entra ID Application resource configuration.')
-type entraIdApplicationConfigurationType = {
- @description('Optional. If the Entra ID Application for website authentication should be enabled or not.')
- enabled: bool?
-}
diff --git a/infra/old/00-older/main2.bicep b/infra/old/00-older/main2.bicep
deleted file mode 100644
index 9d9f3f1ca..000000000
--- a/infra/old/00-older/main2.bicep
+++ /dev/null
@@ -1,54 +0,0 @@
-targetScope = 'subscription'
-
-@minLength(1)
-@maxLength(64)
-@description('Name of the environment that can be used as part of naming resource convention')
-param environmentName string
-
-@minLength(1)
-@description('Primary location for all resources')
-param location string
-
-param backendExists bool
-@secure()
-param backendDefinition object
-param frontendExists bool
-@secure()
-param frontendDefinition object
-
-@description('Id of the user or app to assign application roles')
-param principalId string
-
-// Tags that should be applied to all resources.
-//
-// Note that 'azd-service-name' tags should be applied separately to service host resources.
-// Example usage:
-// tags: union(tags, { 'azd-service-name': })
-var tags = {
- 'azd-env-name': environmentName
-}
-
-// Organize resources in a resource group
-resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
- name: 'rg-${environmentName}'
- location: location
- tags: tags
-}
-
-module resources 'resources.bicep' = {
- scope: rg
- name: 'resources'
- params: {
- location: location
- tags: tags
- principalId: principalId
- backendExists: backendExists
- backendDefinition: backendDefinition
- frontendExists: frontendExists
- frontendDefinition: frontendDefinition
- }
-}
-
-output AZURE_CONTAINER_REGISTRY_ENDPOINT string = resources.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT
-output AZURE_RESOURCE_BACKEND_ID string = resources.outputs.AZURE_RESOURCE_BACKEND_ID
-output AZURE_RESOURCE_FRONTEND_ID string = resources.outputs.AZURE_RESOURCE_FRONTEND_ID
diff --git a/infra/old/00-older/resources.bicep b/infra/old/00-older/resources.bicep
deleted file mode 100644
index 3c9a580c2..000000000
--- a/infra/old/00-older/resources.bicep
+++ /dev/null
@@ -1,242 +0,0 @@
-@description('The location used for all deployed resources')
-param location string = resourceGroup().location
-
-@description('Tags that will be applied to all resources')
-param tags object = {}
-
-
-param backendExists bool
-@secure()
-param backendDefinition object
-param frontendExists bool
-@secure()
-param frontendDefinition object
-
-@description('Id of the user or app to assign application roles')
-param principalId string
-
-var abbrs = loadJsonContent('./abbreviations.json')
-var resourceToken = uniqueString(subscription().id, resourceGroup().id, location)
-
-// Monitor application with Azure Monitor
-module monitoring 'br/public:avm/ptn/azd/monitoring:0.1.0' = {
- name: 'monitoring'
- params: {
- logAnalyticsName: '${abbrs.operationalInsightsWorkspaces}${resourceToken}'
- applicationInsightsName: '${abbrs.insightsComponents}${resourceToken}'
- applicationInsightsDashboardName: '${abbrs.portalDashboards}${resourceToken}'
- location: location
- tags: tags
- }
-}
-
-// Container registry
-module containerRegistry 'br/public:avm/res/container-registry/registry:0.1.1' = {
- name: 'registry'
- params: {
- name: '${abbrs.containerRegistryRegistries}${resourceToken}'
- location: location
- tags: tags
- publicNetworkAccess: 'Enabled'
- roleAssignments:[
- {
- principalId: backendIdentity.outputs.principalId
- principalType: 'ServicePrincipal'
- roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
- }
- {
- principalId: frontendIdentity.outputs.principalId
- principalType: 'ServicePrincipal'
- roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
- }
- ]
- }
-}
-
-// Container apps environment
-module containerAppsEnvironment 'br/public:avm/res/app/managed-environment:0.4.5' = {
- name: 'container-apps-environment'
- params: {
- logAnalyticsWorkspaceResourceId: monitoring.outputs.logAnalyticsWorkspaceResourceId
- name: '${abbrs.appManagedEnvironments}${resourceToken}'
- location: location
- zoneRedundant: false
- }
-}
-
-module backendIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = {
- name: 'backendidentity'
- params: {
- name: '${abbrs.managedIdentityUserAssignedIdentities}backend-${resourceToken}'
- location: location
- }
-}
-
-module backendFetchLatestImage './modules/fetch-container-image.bicep' = {
- name: 'backend-fetch-image'
- params: {
- exists: backendExists
- name: 'backend'
- }
-}
-
-var backendAppSettingsArray = filter(array(backendDefinition.settings), i => i.name != '')
-var backendSecrets = map(filter(backendAppSettingsArray, i => i.?secret != null), i => {
- name: i.name
- value: i.value
- secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
-})
-var backendEnv = map(filter(backendAppSettingsArray, i => i.?secret == null), i => {
- name: i.name
- value: i.value
-})
-
-module backend 'br/public:avm/res/app/container-app:0.8.0' = {
- name: 'backend'
- params: {
- name: 'backend'
- ingressTargetPort: 8000
- scaleMinReplicas: 1
- scaleMaxReplicas: 10
- secrets: {
- secureList: union([
- ],
- map(backendSecrets, secret => {
- name: secret.secretRef
- value: secret.value
- }))
- }
- containers: [
- {
- image: backendFetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
- name: 'main'
- resources: {
- cpu: json('0.5')
- memory: '1.0Gi'
- }
- env: union([
- {
- name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
- value: monitoring.outputs.applicationInsightsConnectionString
- }
- {
- name: 'AZURE_CLIENT_ID'
- value: backendIdentity.outputs.clientId
- }
- {
- name: 'PORT'
- value: '8000'
- }
- ],
- backendEnv,
- map(backendSecrets, secret => {
- name: secret.name
- secretRef: secret.secretRef
- }))
- }
- ]
- managedIdentities:{
- systemAssigned: false
- userAssignedResourceIds: [backendIdentity.outputs.resourceId]
- }
- registries:[
- {
- server: containerRegistry.outputs.loginServer
- identity: backendIdentity.outputs.resourceId
- }
- ]
- environmentResourceId: containerAppsEnvironment.outputs.resourceId
- location: location
- tags: union(tags, { 'azd-service-name': 'backend' })
- }
-}
-
-module frontendIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = {
- name: 'frontendidentity'
- params: {
- name: '${abbrs.managedIdentityUserAssignedIdentities}frontend-${resourceToken}'
- location: location
- }
-}
-
-module frontendFetchLatestImage './modules/fetch-container-image.bicep' = {
- name: 'frontend-fetch-image'
- params: {
- exists: frontendExists
- name: 'frontend'
- }
-}
-
-var frontendAppSettingsArray = filter(array(frontendDefinition.settings), i => i.name != '')
-var frontendSecrets = map(filter(frontendAppSettingsArray, i => i.?secret != null), i => {
- name: i.name
- value: i.value
- secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
-})
-var frontendEnv = map(filter(frontendAppSettingsArray, i => i.?secret == null), i => {
- name: i.name
- value: i.value
-})
-
-module frontend 'br/public:avm/res/app/container-app:0.8.0' = {
- name: 'frontend'
- params: {
- name: 'frontend'
- ingressTargetPort: 3000
- scaleMinReplicas: 1
- scaleMaxReplicas: 10
- secrets: {
- secureList: union([
- ],
- map(frontendSecrets, secret => {
- name: secret.secretRef
- value: secret.value
- }))
- }
- containers: [
- {
- image: frontendFetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
- name: 'main'
- resources: {
- cpu: json('0.5')
- memory: '1.0Gi'
- }
- env: union([
- {
- name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
- value: monitoring.outputs.applicationInsightsConnectionString
- }
- {
- name: 'AZURE_CLIENT_ID'
- value: frontendIdentity.outputs.clientId
- }
- {
- name: 'PORT'
- value: '3000'
- }
- ],
- frontendEnv,
- map(frontendSecrets, secret => {
- name: secret.name
- secretRef: secret.secretRef
- }))
- }
- ]
- managedIdentities:{
- systemAssigned: false
- userAssignedResourceIds: [frontendIdentity.outputs.resourceId]
- }
- registries:[
- {
- server: containerRegistry.outputs.loginServer
- identity: frontendIdentity.outputs.resourceId
- }
- ]
- environmentResourceId: containerAppsEnvironment.outputs.resourceId
- location: location
- tags: union(tags, { 'azd-service-name': 'frontend' })
- }
-}
-output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer
-output AZURE_RESOURCE_BACKEND_ID string = backend.outputs.resourceId
-output AZURE_RESOURCE_FRONTEND_ID string = frontend.outputs.resourceId
diff --git a/infra/old/08-2025/abbreviations.json b/infra/old/08-2025/abbreviations.json
deleted file mode 100644
index 93b95656b..000000000
--- a/infra/old/08-2025/abbreviations.json
+++ /dev/null
@@ -1,227 +0,0 @@
-{
- "ai": {
- "aiSearch": "srch-",
- "aiServices": "aisa-",
- "aiVideoIndexer": "avi-",
- "machineLearningWorkspace": "mlw-",
- "openAIService": "oai-",
- "botService": "bot-",
- "computerVision": "cv-",
- "contentModerator": "cm-",
- "contentSafety": "cs-",
- "customVisionPrediction": "cstv-",
- "customVisionTraining": "cstvt-",
- "documentIntelligence": "di-",
- "faceApi": "face-",
- "healthInsights": "hi-",
- "immersiveReader": "ir-",
- "languageService": "lang-",
- "speechService": "spch-",
- "translator": "trsl-",
- "aiHub": "aih-",
- "aiHubProject": "aihp-"
- },
- "analytics": {
- "analysisServicesServer": "as",
- "databricksWorkspace": "dbw-",
- "dataExplorerCluster": "dec",
- "dataExplorerClusterDatabase": "dedb",
- "dataFactory": "adf-",
- "digitalTwin": "dt-",
- "streamAnalytics": "asa-",
- "synapseAnalyticsPrivateLinkHub": "synplh-",
- "synapseAnalyticsSQLDedicatedPool": "syndp",
- "synapseAnalyticsSparkPool": "synsp",
- "synapseAnalyticsWorkspaces": "synw",
- "dataLakeStoreAccount": "dls",
- "dataLakeAnalyticsAccount": "dla",
- "eventHubsNamespace": "evhns-",
- "eventHub": "evh-",
- "eventGridDomain": "evgd-",
- "eventGridSubscriptions": "evgs-",
- "eventGridTopic": "evgt-",
- "eventGridSystemTopic": "egst-",
- "hdInsightHadoopCluster": "hadoop-",
- "hdInsightHBaseCluster": "hbase-",
- "hdInsightKafkaCluster": "kafka-",
- "hdInsightSparkCluster": "spark-",
- "hdInsightStormCluster": "storm-",
- "hdInsightMLServicesCluster": "mls-",
- "iotHub": "iot-",
- "provisioningServices": "provs-",
- "provisioningServicesCertificate": "pcert-",
- "powerBIEmbedded": "pbi-",
- "timeSeriesInsightsEnvironment": "tsi-"
- },
- "compute": {
- "appServiceEnvironment": "ase-",
- "appServicePlan": "asp-",
- "loadTesting": "lt-",
- "availabilitySet": "avail-",
- "arcEnabledServer": "arcs-",
- "arcEnabledKubernetesCluster": "arck",
- "batchAccounts": "ba-",
- "cloudService": "cld-",
- "communicationServices": "acs-",
- "diskEncryptionSet": "des",
- "functionApp": "func-",
- "gallery": "gal",
- "hostingEnvironment": "host-",
- "imageTemplate": "it-",
- "managedDiskOS": "osdisk",
- "managedDiskData": "disk",
- "notificationHubs": "ntf-",
- "notificationHubsNamespace": "ntfns-",
- "proximityPlacementGroup": "ppg-",
- "restorePointCollection": "rpc-",
- "snapshot": "snap-",
- "staticWebApp": "stapp-",
- "virtualMachine": "vm",
- "virtualMachineScaleSet": "vmss-",
- "virtualMachineMaintenanceConfiguration": "mc-",
- "virtualMachineStorageAccount": "stvm",
- "webApp": "app-"
- },
- "containers": {
- "aksCluster": "aks-",
- "aksSystemNodePool": "npsystem-",
- "aksUserNodePool": "np-",
- "containerApp": "ca-",
- "containerAppsEnvironment": "cae-",
- "containerRegistry": "cr",
- "containerInstance": "ci",
- "serviceFabricCluster": "sf-",
- "serviceFabricManagedCluster": "sfmc-"
- },
- "databases": {
- "cosmosDBDatabase": "cosmos-",
- "cosmosDBApacheCassandra": "coscas-",
- "cosmosDBMongoDB": "cosmon-",
- "cosmosDBNoSQL": "cosno-",
- "cosmosDBTable": "costab-",
- "cosmosDBGremlin": "cosgrm-",
- "cosmosDBPostgreSQL": "cospos-",
- "cacheForRedis": "redis-",
- "sqlDatabaseServer": "sql-",
- "sqlDatabase": "sqldb-",
- "sqlElasticJobAgent": "sqlja-",
- "sqlElasticPool": "sqlep-",
- "mariaDBServer": "maria-",
- "mariaDBDatabase": "mariadb-",
- "mySQLDatabase": "mysql-",
- "postgreSQLDatabase": "psql-",
- "sqlServerStretchDatabase": "sqlstrdb-",
- "sqlManagedInstance": "sqlmi-"
- },
- "developerTools": {
- "appConfigurationStore": "appcs-",
- "mapsAccount": "map-",
- "signalR": "sigr",
- "webPubSub": "wps-"
- },
- "devOps": {
- "managedGrafana": "amg-"
- },
- "integration": {
- "apiManagementService": "apim-",
- "integrationAccount": "ia-",
- "logicApp": "logic-",
- "serviceBusNamespace": "sbns-",
- "serviceBusQueue": "sbq-",
- "serviceBusTopic": "sbt-",
- "serviceBusTopicSubscription": "sbts-"
- },
- "managementGovernance": {
- "automationAccount": "aa-",
- "applicationInsights": "appi-",
- "monitorActionGroup": "ag-",
- "monitorDataCollectionRules": "dcr-",
- "monitorAlertProcessingRule": "apr-",
- "blueprint": "bp-",
- "blueprintAssignment": "bpa-",
- "dataCollectionEndpoint": "dce-",
- "logAnalyticsWorkspace": "log-",
- "logAnalyticsQueryPacks": "pack-",
- "managementGroup": "mg-",
- "purviewInstance": "pview-",
- "resourceGroup": "rg-",
- "templateSpecsName": "ts-"
- },
- "migration": {
- "migrateProject": "migr-",
- "databaseMigrationService": "dms-",
- "recoveryServicesVault": "rsv-"
- },
- "networking": {
- "applicationGateway": "agw-",
- "applicationSecurityGroup": "asg-",
- "cdnProfile": "cdnp-",
- "cdnEndpoint": "cdne-",
- "connections": "con-",
- "dnsForwardingRuleset": "dnsfrs-",
- "dnsPrivateResolver": "dnspr-",
- "dnsPrivateResolverInboundEndpoint": "in-",
- "dnsPrivateResolverOutboundEndpoint": "out-",
- "firewall": "afw-",
- "firewallPolicy": "afwp-",
- "expressRouteCircuit": "erc-",
- "expressRouteGateway": "ergw-",
- "frontDoorProfile": "afd-",
- "frontDoorEndpoint": "fde-",
- "frontDoorFirewallPolicy": "fdfp-",
- "ipGroups": "ipg-",
- "loadBalancerInternal": "lbi-",
- "loadBalancerExternal": "lbe-",
- "loadBalancerRule": "rule-",
- "localNetworkGateway": "lgw-",
- "natGateway": "ng-",
- "networkInterface": "nic-",
- "networkSecurityGroup": "nsg-",
- "networkSecurityGroupSecurityRules": "nsgsr-",
- "networkWatcher": "nw-",
- "privateLink": "pl-",
- "privateEndpoint": "pep-",
- "publicIPAddress": "pip-",
- "publicIPAddressPrefix": "ippre-",
- "routeFilter": "rf-",
- "routeServer": "rtserv-",
- "routeTable": "rt-",
- "serviceEndpointPolicy": "se-",
- "trafficManagerProfile": "traf-",
- "userDefinedRoute": "udr-",
- "virtualNetwork": "vnet-",
- "virtualNetworkGateway": "vgw-",
- "virtualNetworkManager": "vnm-",
- "virtualNetworkPeering": "peer-",
- "virtualNetworkSubnet": "snet-",
- "virtualWAN": "vwan-",
- "virtualWANHub": "vhub-"
- },
- "security": {
- "bastion": "bas-",
- "keyVault": "kv-",
- "keyVaultManagedHSM": "kvmhsm-",
- "managedIdentity": "id-",
- "sshKey": "sshkey-",
- "vpnGateway": "vpng-",
- "vpnConnection": "vcn-",
- "vpnSite": "vst-",
- "webApplicationFirewallPolicy": "waf",
- "webApplicationFirewallPolicyRuleGroup": "wafrg"
- },
- "storage": {
- "storSimple": "ssimp",
- "backupVault": "bvault-",
- "backupVaultPolicy": "bkpol-",
- "fileShare": "share-",
- "storageAccount": "st",
- "storageSyncService": "sss-"
- },
- "virtualDesktop": {
- "labServicesPlan": "lp-",
- "virtualDesktopHostPool": "vdpool-",
- "virtualDesktopApplicationGroup": "vdag-",
- "virtualDesktopWorkspace": "vdws-",
- "virtualDesktopScalingPlan": "vdscaling-"
- }
- }
\ No newline at end of file
diff --git a/infra/old/08-2025/bicepconfig.json b/infra/old/08-2025/bicepconfig.json
deleted file mode 100644
index 7d7839f72..000000000
--- a/infra/old/08-2025/bicepconfig.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "experimentalFeaturesEnabled": {
- "extensibility": true
- },
- "extensions": {
- "graphV1": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.2.0-preview" // ,
- // "graphBeta": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/beta:0.2.0-preview"
- }
- }
\ No newline at end of file
diff --git a/infra/old/08-2025/main.bicep b/infra/old/08-2025/main.bicep
deleted file mode 100644
index 8ee54772d..000000000
--- a/infra/old/08-2025/main.bicep
+++ /dev/null
@@ -1,1720 +0,0 @@
-metadata name = 'Multi-Agent Custom Automation Engine'
-metadata description = 'This module contains the resources required to deploy the Multi-Agent Custom Automation Engine solution accelerator for both Sandbox environments and WAF aligned environments.'
-
-@description('Set to true if you want to deploy WAF-aligned infrastructure.')
-param useWafAlignedArchitecture bool
-
-@description('Use this parameter to use an existing AI project resource ID')
-param existingFoundryProjectResourceId string = ''
-
-@description('Required. Name of the environment to deploy the solution into.')
-param environmentName string
-
-@description('Required. Location for all Resources except AI Foundry.')
-param solutionLocation string = resourceGroup().location
-
-@description('Optional. Enable/Disable usage telemetry for module.')
-param enableTelemetry bool = true
-
-param existingLogAnalyticsWorkspaceId string = ''
-
-// Restricting deployment to only supported Azure OpenAI regions validated with GPT-4o model
-@metadata({
- azd : {
- type: 'location'
- usageName : [
- 'OpenAI.GlobalStandard.gpt-4o, 150'
- ]
- }
-})
-@allowed(['australiaeast', 'eastus2', 'francecentral', 'japaneast', 'norwayeast', 'swedencentral', 'uksouth', 'westus'])
-@description('Azure OpenAI Location')
-param aiDeploymentsLocation string
-
-@minLength(1)
-@description('Name of the GPT model to deploy:')
-param gptModelName string = 'gpt-4o'
-
-param gptModelVersion string = '2024-08-06'
-
-@minLength(1)
-@description('GPT model deployment type:')
-param modelDeploymentType string = 'GlobalStandard'
-
-@description('Optional. AI model deployment token capacity.')
-param gptModelCapacity int = 150
-
-@description('Set the image tag for the container images used in the solution. Default is "latest".')
-param imageTag string = 'latest'
-
-param solutionPrefix string = 'macae-${padLeft(take(toLower(uniqueString(subscription().id, environmentName, resourceGroup().location, resourceGroup().name)), 12), 12, '0')}'
-
-@description('Optional. The tags to apply to all deployed Azure resources.')
-param tags object = {
- app: solutionPrefix
- location: solutionLocation
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Log Analytics Workspace resource.')
-param logAnalyticsWorkspaceConfiguration logAnalyticsWorkspaceConfigurationType = {
- enabled: true
- name: 'log-${solutionPrefix}'
- location: solutionLocation
- sku: 'PerGB2018'
- tags: tags
- dataRetentionInDays: useWafAlignedArchitecture ? 365 : 30
- existingWorkspaceResourceId: existingLogAnalyticsWorkspaceId
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Application Insights resource.')
-param applicationInsightsConfiguration applicationInsightsConfigurationType = {
- enabled: true
- name: 'appi-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- retentionInDays: useWafAlignedArchitecture ? 365 : 30
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Managed Identity resource.')
-param userAssignedManagedIdentityConfiguration userAssignedManagedIdentityType = {
- enabled: true
- name: 'id-${solutionPrefix}'
- location: solutionLocation
- tags: tags
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the backend subnet.')
-param networkSecurityGroupBackendConfiguration networkSecurityGroupConfigurationType = {
- enabled: true
- name: 'nsg-backend-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- securityRules: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the containers subnet.')
-param networkSecurityGroupContainersConfiguration networkSecurityGroupConfigurationType = {
- enabled: true
- name: 'nsg-containers-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- securityRules: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the Bastion subnet.')
-param networkSecurityGroupBastionConfiguration networkSecurityGroupConfigurationType = {
- enabled: true
- name: 'nsg-bastion-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- securityRules: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine Network Security Group resource for the administration subnet.')
-param networkSecurityGroupAdministrationConfiguration networkSecurityGroupConfigurationType = {
- enabled: true
- name: 'nsg-administration-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- securityRules: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine virtual network resource.')
-param virtualNetworkConfiguration virtualNetworkConfigurationType = {
- enabled: useWafAlignedArchitecture ? true : false
- name: 'vnet-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- addressPrefixes: null //Default value set on module configuration
- subnets: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Multi-Agent Custom Automation Engine bastion resource.')
-param bastionConfiguration bastionConfigurationType = {
- enabled: true
- name: 'bas-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- sku: 'Standard'
- virtualNetworkResourceId: null //Default value set on module configuration
- publicIpResourceName: 'pip-bas${solutionPrefix}'
-}
-
-@description('Optional. Configuration for the Windows virtual machine.')
-param virtualMachineConfiguration virtualMachineConfigurationType = {
- enabled: true
- name: 'vm${solutionPrefix}'
- location: solutionLocation
- tags: tags
- adminUsername: 'adminuser'
- adminPassword: useWafAlignedArchitecture? 'P@ssw0rd1234' : guid(solutionPrefix, subscription().subscriptionId)
- vmSize: 'Standard_D2s_v3'
- subnetResourceId: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the AI Foundry AI Services resource.')
-param aiFoundryAiServicesConfiguration aiServicesConfigurationType = {
- enabled: true
- name: 'aisa-${solutionPrefix}'
- location: aiDeploymentsLocation
- sku: 'S0'
- deployments: null //Default value set on module configuration
- subnetResourceId: null //Default value set on module configuration
- modelCapacity: gptModelCapacity
-}
-
-@description('Optional. The configuration to apply for the AI Foundry AI Project resource.')
-param aiFoundryAiProjectConfiguration aiProjectConfigurationType = {
- enabled: true
- name: 'aifp-${solutionPrefix}'
- location: aiDeploymentsLocation
- sku: 'Basic'
- tags: tags
-}
-
-@description('Optional. The configuration to apply for the Cosmos DB Account resource.')
-param cosmosDbAccountConfiguration cosmosDbAccountConfigurationType = {
- enabled: true
- name: 'cosmos-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- subnetResourceId: null //Default value set on module configuration
- sqlDatabases: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Container App Environment resource.')
-param containerAppEnvironmentConfiguration containerAppEnvironmentConfigurationType = {
- enabled: true
- name: 'cae-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- subnetResourceId: null //Default value set on module configuration
-}
-
-@description('Optional. The configuration to apply for the Container App resource.')
-param containerAppConfiguration containerAppConfigurationType = {
- enabled: true
- name: 'ca-${solutionPrefix}'
- location: solutionLocation
- tags: tags
- environmentResourceId: null //Default value set on module configuration
- concurrentRequests: '100'
- containerCpu: '2.0'
- containerMemory: '4.0Gi'
- containerImageRegistryDomain: 'biabcontainerreg.azurecr.io'
- containerImageName: 'macaebackend'
- containerImageTag: imageTag
- containerName: 'backend'
- ingressTargetPort: 8000
- maxReplicas: 1
- minReplicas: 1
-}
-
-@description('Optional. The configuration to apply for the Web Server Farm resource.')
-param webServerFarmConfiguration webServerFarmConfigurationType = {
- enabled: true
- name: 'asp-${solutionPrefix}'
- location: solutionLocation
- skuName: useWafAlignedArchitecture? 'P1v3' : 'B2'
- skuCapacity: useWafAlignedArchitecture ? 3 : 1
- tags: tags
-}
-
-@description('Optional. The configuration to apply for the Web Server Farm resource.')
-param webSiteConfiguration webSiteConfigurationType = {
- enabled: true
- name: 'app-${solutionPrefix}'
- location: solutionLocation
- containerImageRegistryDomain: 'biabcontainerreg.azurecr.io'
- containerImageName: 'macaefrontend'
- containerImageTag: imageTag
- containerName: 'backend'
- tags: tags
- environmentResourceId: null //Default value set on module configuration
-}
-
-// ========== Resource Group Tag ========== //
-resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = {
- name: 'default'
- properties: {
- tags: {
- ...tags
- TemplateName: 'Macae'
- }
- }
-}
-
-// ========== Log Analytics Workspace ========== //
-// WAF best practices for Log Analytics: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-log-analytics
-// Log Analytics configuration defaults
-var logAnalyticsWorkspaceEnabled = logAnalyticsWorkspaceConfiguration.?enabled ?? true
-var logAnalyticsWorkspaceResourceName = logAnalyticsWorkspaceConfiguration.?name ?? 'log-${solutionPrefix}'
-var existingWorkspaceResourceId = logAnalyticsWorkspaceConfiguration.?existingWorkspaceResourceId ?? ''
-var useExistingWorkspace = existingWorkspaceResourceId != ''
-
-module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.2' = if (logAnalyticsWorkspaceEnabled && !useExistingWorkspace) {
- name: take('avm.res.operational-insights.workspace.${logAnalyticsWorkspaceResourceName}', 64)
- params: {
- name: logAnalyticsWorkspaceResourceName
- tags: logAnalyticsWorkspaceConfiguration.?tags ?? tags
- location: logAnalyticsWorkspaceConfiguration.?location ?? solutionLocation
- enableTelemetry: enableTelemetry
- skuName: logAnalyticsWorkspaceConfiguration.?sku ?? 'PerGB2018'
- dataRetention: logAnalyticsWorkspaceConfiguration.?dataRetentionInDays ?? 365
- diagnosticSettings: [{ useThisWorkspace: true }]
- }
-}
-
-var logAnalyticsWorkspaceId = useExistingWorkspace ? existingWorkspaceResourceId : logAnalyticsWorkspace.outputs.resourceId
-
-// ========== Application Insights ========== //
-// WAF best practices for Application Insights: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/application-insights
-// Application Insights configuration defaults
-var applicationInsightsEnabled = applicationInsightsConfiguration.?enabled ?? true
-var applicationInsightsResourceName = applicationInsightsConfiguration.?name ?? 'appi-${solutionPrefix}'
-module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (applicationInsightsEnabled) {
- name: take('avm.res.insights.component.${applicationInsightsResourceName}', 64)
- params: {
- name: applicationInsightsResourceName
- workspaceResourceId: logAnalyticsWorkspaceId
- location: applicationInsightsConfiguration.?location ?? solutionLocation
- enableTelemetry: enableTelemetry
- tags: applicationInsightsConfiguration.?tags ?? tags
- retentionInDays: applicationInsightsConfiguration.?retentionInDays ?? 365
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- kind: 'web'
- disableIpMasking: false
- flowType: 'Bluefield'
- }
-}
-
-// ========== User assigned identity Web Site ========== //
-// WAF best practices for identity and access management: https://learn.microsoft.com/en-us/azure/well-architected/security/identity-access
-var userAssignedManagedIdentityEnabled = userAssignedManagedIdentityConfiguration.?enabled ?? true
-var userAssignedManagedIdentityResourceName = userAssignedManagedIdentityConfiguration.?name ?? 'id-${solutionPrefix}'
-module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = if (userAssignedManagedIdentityEnabled) {
- name: take('avm.res.managed-identity.user-assigned-identity.${userAssignedManagedIdentityResourceName}', 64)
- params: {
- name: userAssignedManagedIdentityResourceName
- tags: userAssignedManagedIdentityConfiguration.?tags ?? tags
- location: userAssignedManagedIdentityConfiguration.?location ?? solutionLocation
- enableTelemetry: enableTelemetry
- }
-}
-
-// ========== Network Security Groups ========== //
-// WAF best practices for virtual networks: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/virtual-network
-// WAF recommendations for networking and connectivity: https://learn.microsoft.com/en-us/azure/well-architected/security/networking
-var networkSecurityGroupBackendEnabled = networkSecurityGroupBackendConfiguration.?enabled ?? true
-var networkSecurityGroupBackendResourceName = networkSecurityGroupBackendConfiguration.?name ?? 'nsg-backend-${solutionPrefix}'
-module networkSecurityGroupBackend 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupBackendEnabled) {
- name: take('avm.res.network.network-security-group.${networkSecurityGroupBackendResourceName}', 64)
- params: {
- name: networkSecurityGroupBackendResourceName
- location: networkSecurityGroupBackendConfiguration.?location ?? solutionLocation
- tags: networkSecurityGroupBackendConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- securityRules: networkSecurityGroupBackendConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
- }
-}
-
-var networkSecurityGroupContainersEnabled = networkSecurityGroupContainersConfiguration.?enabled ?? true
-var networkSecurityGroupContainersResourceName = networkSecurityGroupContainersConfiguration.?name ?? 'nsg-containers-${solutionPrefix}'
-module networkSecurityGroupContainers 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupContainersEnabled) {
- name: take('avm.res.network.network-security-group.${networkSecurityGroupContainersResourceName}', 64)
- params: {
- name: networkSecurityGroupContainersResourceName
- location: networkSecurityGroupContainersConfiguration.?location ?? solutionLocation
- tags: networkSecurityGroupContainersConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- securityRules: networkSecurityGroupContainersConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
- }
-}
-
-var networkSecurityGroupBastionEnabled = networkSecurityGroupBastionConfiguration.?enabled ?? true
-var networkSecurityGroupBastionResourceName = networkSecurityGroupBastionConfiguration.?name ?? 'nsg-bastion-${solutionPrefix}'
-module networkSecurityGroupBastion 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupBastionEnabled) {
- name: take('avm.res.network.network-security-group.${networkSecurityGroupBastionResourceName}', 64)
- params: {
- name: networkSecurityGroupBastionResourceName
- location: networkSecurityGroupBastionConfiguration.?location ?? solutionLocation
- tags: networkSecurityGroupBastionConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- securityRules: networkSecurityGroupBastionConfiguration.?securityRules ?? [
- {
- name: 'AllowHttpsInBound'
- properties: {
- protocol: 'Tcp'
- sourcePortRange: '*'
- sourceAddressPrefix: 'Internet'
- 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'
- }
- }
- ]
- }
-}
-
-var networkSecurityGroupAdministrationEnabled = networkSecurityGroupAdministrationConfiguration.?enabled ?? true
-var networkSecurityGroupAdministrationResourceName = networkSecurityGroupAdministrationConfiguration.?name ?? 'nsg-administration-${solutionPrefix}'
-module networkSecurityGroupAdministration 'br/public:avm/res/network/network-security-group:0.5.1' = if (virtualNetworkEnabled && networkSecurityGroupAdministrationEnabled) {
- name: take('avm.res.network.network-security-group.${networkSecurityGroupAdministrationResourceName}', 64)
- params: {
- name: networkSecurityGroupAdministrationResourceName
- location: networkSecurityGroupAdministrationConfiguration.?location ?? solutionLocation
- tags: networkSecurityGroupAdministrationConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- securityRules: networkSecurityGroupAdministrationConfiguration.?securityRules ?? [
- // {
- // name: 'DenySshRdpOutbound' //Azure Bastion
- // properties: {
- // priority: 200
- // access: 'Deny'
- // protocol: '*'
- // direction: 'Outbound'
- // sourceAddressPrefix: 'VirtualNetwork'
- // sourcePortRange: '*'
- // destinationAddressPrefix: '*'
- // destinationPortRanges: [
- // '3389'
- // '22'
- // ]
- // }
- // }
- ]
- }
-}
-
-// ========== Virtual Network ========== //
-// WAF best practices for virtual networks: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/virtual-network
-// WAF recommendations for networking and connectivity: https://learn.microsoft.com/en-us/azure/well-architected/security/networking
-var virtualNetworkEnabled = virtualNetworkConfiguration.?enabled ?? true
-var virtualNetworkResourceName = virtualNetworkConfiguration.?name ?? 'vnet-${solutionPrefix}'
-module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = if (virtualNetworkEnabled) {
- name: take('avm.res.network.virtual-network.${virtualNetworkResourceName}', 64)
- params: {
- name: virtualNetworkResourceName
- location: virtualNetworkConfiguration.?location ?? solutionLocation
- tags: virtualNetworkConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- addressPrefixes: virtualNetworkConfiguration.?addressPrefixes ?? ['10.0.0.0/8']
- subnets: virtualNetworkConfiguration.?subnets ?? [
- {
- name: 'backend'
- addressPrefix: '10.0.0.0/27'
- //defaultOutboundAccess: false TODO: check this configuration for a more restricted outbound access
- networkSecurityGroupResourceId: networkSecurityGroupBackend.outputs.resourceId
- }
- {
- name: 'administration'
- addressPrefix: '10.0.0.32/27'
- networkSecurityGroupResourceId: networkSecurityGroupAdministration.outputs.resourceId
- }
- {
- // For Azure Bastion resources deployed on or after November 2, 2021, the minimum AzureBastionSubnet size is /26 or larger (/25, /24, etc.).
- // https://learn.microsoft.com/en-us/azure/bastion/configuration-settings#subnet
- name: 'AzureBastionSubnet' //This exact name is required for Azure Bastion
- addressPrefix: '10.0.0.64/26'
- networkSecurityGroupResourceId: networkSecurityGroupBastion.outputs.resourceId
- }
- {
- // If you use your own vnw, you need to provide a subnet that is dedicated exclusively to the Container App environment you deploy. This subnet isn't available to other services
- // https://learn.microsoft.com/en-us/azure/container-apps/networking?tabs=workload-profiles-env%2Cazure-cli#custom-vnw-configuration
- name: 'containers'
- addressPrefix: '10.0.2.0/23' //subnet of size /23 is required for container app
- delegation: 'Microsoft.App/environments'
- networkSecurityGroupResourceId: networkSecurityGroupContainers.outputs.resourceId
- privateEndpointNetworkPolicies: 'Disabled'
- privateLinkServiceNetworkPolicies: 'Enabled'
- }
- ]
- }
-}
-var bastionEnabled = bastionConfiguration.?enabled ?? true
-var bastionResourceName = bastionConfiguration.?name ?? 'bas-${solutionPrefix}'
-
-// ========== Bastion host ========== //
-// WAF best practices for virtual networks: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/virtual-network
-// WAF recommendations for networking and connectivity: https://learn.microsoft.com/en-us/azure/well-architected/security/networking
-module bastionHost 'br/public:avm/res/network/bastion-host:0.6.1' = if (virtualNetworkEnabled && bastionEnabled) {
- name: take('avm.res.network.bastion-host.${bastionResourceName}', 64)
- params: {
- name: bastionResourceName
- location: bastionConfiguration.?location ?? solutionLocation
- skuName: bastionConfiguration.?sku ?? 'Standard'
- enableTelemetry: enableTelemetry
- tags: bastionConfiguration.?tags ?? tags
- virtualNetworkResourceId: bastionConfiguration.?virtualNetworkResourceId ?? virtualNetwork.?outputs.?resourceId
- publicIPAddressObject: {
- name: bastionConfiguration.?publicIpResourceName ?? 'pip-bas${solutionPrefix}'
- zones: []
- }
- disableCopyPaste: false
- enableFileCopy: false
- enableIpConnect: true
- enableShareableLink: true
- }
-}
-
-// ========== Virtual machine ========== //
-// WAF best practices for virtual machines: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/virtual-machines
-var virtualMachineEnabled = virtualMachineConfiguration.?enabled ?? true
-var virtualMachineResourceName = virtualMachineConfiguration.?name ?? 'vm${solutionPrefix}'
-module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.13.0' = if (virtualNetworkEnabled && virtualMachineEnabled) {
- name: take('avm.res.compute.virtual-machine.${virtualMachineResourceName}', 64)
- params: {
- name: virtualMachineResourceName
- computerName: take(virtualMachineResourceName, 15)
- location: virtualMachineConfiguration.?location ?? solutionLocation
- tags: virtualMachineConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- vmSize: virtualMachineConfiguration.?vmSize ?? 'Standard_D2s_v3'
- adminUsername: virtualMachineConfiguration.?adminUsername ?? 'adminuser'
- adminPassword: virtualMachineConfiguration.?adminPassword ?? guid(solutionPrefix, subscription().subscriptionId)
- nicConfigurations: [
- {
- name: 'nic-${virtualMachineResourceName}'
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- ipConfigurations: [
- {
- name: '${virtualMachineResourceName}-nic01-ipconfig01'
- subnetResourceId: virtualMachineConfiguration.?subnetResourceId ?? virtualNetwork.outputs.subnetResourceIds[1]
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- }
- ]
- }
- ]
- imageReference: {
- publisher: 'microsoft-dsvm'
- offer: 'dsvm-win-2022'
- sku: 'winserver-2022'
- version: 'latest'
- }
- osDisk: {
- name: 'osdisk-${virtualMachineResourceName}'
- createOption: 'FromImage'
- managedDisk: {
- storageAccountType: 'Standard_LRS'
- }
- diskSizeGB: 128
- caching: 'ReadWrite'
- }
- osType: 'Windows'
- encryptionAtHost: false //The property 'securityProfile.encryptionAtHost' is not valid because the 'Microsoft.Compute/EncryptionAtHost' feature is not enabled for this subscription.
- zone: 0
- extensionAadJoinConfig: {
- enabled: true
- typeHandlerVersion: '1.0'
- }
- }
-}
-
-// ========== AI Foundry: AI Services ========== //
-// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai
-var openAiSubResource = 'account'
-var openAiPrivateDnsZones = {
- 'privatelink.cognitiveservices.azure.com': openAiSubResource
- 'privatelink.openai.azure.com': openAiSubResource
- 'privatelink.services.ai.azure.com': openAiSubResource
-}
-
-module privateDnsZonesAiServices 'br/public:avm/res/network/private-dns-zone:0.7.1' = [
- for zone in objectKeys(openAiPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryAIservicesEnabled) {
- name: take(
- 'avm.res.network.private-dns-zone.ai-services.${uniqueString(aiFoundryAiServicesResourceName,zone)}.${solutionPrefix}',
- 64
- )
- params: {
- name: zone
- tags: tags
- enableTelemetry: enableTelemetry
- virtualNetworkLinks: [
- {
- name: 'vnetlink-${split(zone, '.')[1]}'
- virtualNetworkResourceId: virtualNetwork.outputs.resourceId
- }
- ]
- }
- }
-]
-
-// NOTE: Required version 'Microsoft.CognitiveServices/accounts@2024-04-01-preview' not available in AVM
-var useExistingFoundryProject = !empty(existingFoundryProjectResourceId)
-var existingAiFoundryName = useExistingFoundryProject?split( existingFoundryProjectResourceId,'/')[8]:''
-var aiFoundryAiServicesResourceName = useExistingFoundryProject? existingAiFoundryName : aiFoundryAiServicesConfiguration.?name ?? 'aisa-${solutionPrefix}'
-var aiFoundryAIservicesEnabled = aiFoundryAiServicesConfiguration.?enabled ?? true
-var aiFoundryAiServicesModelDeployment = {
- format: 'OpenAI'
- name: gptModelName
- version: gptModelVersion
- sku: {
- name: modelDeploymentType
- //Curently the capacity is set to 140 for opinanal performance.
- capacity: aiFoundryAiServicesConfiguration.?modelCapacity ?? gptModelCapacity
- }
- raiPolicyName: 'Microsoft.Default'
-}
-
-module aiFoundryAiServices 'modules/account/main.bicep' = if (aiFoundryAIservicesEnabled) {
- name: take('avm.res.cognitive-services.account.${aiFoundryAiServicesResourceName}', 64)
- params: {
- name: aiFoundryAiServicesResourceName
- tags: aiFoundryAiServicesConfiguration.?tags ?? tags
- location: aiFoundryAiServicesConfiguration.?location ?? aiDeploymentsLocation
- enableTelemetry: enableTelemetry
- projectName: 'aifp-${solutionPrefix}'
- projectDescription: 'aifp-${solutionPrefix}'
- existingFoundryProjectResourceId: existingFoundryProjectResourceId
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- sku: aiFoundryAiServicesConfiguration.?sku ?? 'S0'
- kind: 'AIServices'
- disableLocalAuth: true //Should be set to true for WAF aligned configuration
- customSubDomainName: aiFoundryAiServicesResourceName
- apiProperties: {
- //staticsEnabled: false
- }
- allowProjectManagement: true
- managedIdentities: {
- systemAssigned: true
- }
- publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled'
- networkAcls: {
- bypass: 'AzureServices'
- defaultAction: (virtualNetworkEnabled) ? 'Deny' : 'Allow'
- }
- privateEndpoints: virtualNetworkEnabled && !useExistingFoundryProject
- ? ([
- {
- name: 'pep-${aiFoundryAiServicesResourceName}'
- customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}'
- subnetResourceId: aiFoundryAiServicesConfiguration.?subnetResourceId ?? virtualNetwork.outputs.subnetResourceIds[0]
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: map(objectKeys(openAiPrivateDnsZones), zone => {
- name: replace(zone, '.', '-')
- privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone)
- })
- }
- }
- ])
- : []
- deployments: aiFoundryAiServicesConfiguration.?deployments ?? [
- {
- name: aiFoundryAiServicesModelDeployment.name
- model: {
- format: aiFoundryAiServicesModelDeployment.format
- name: aiFoundryAiServicesModelDeployment.name
- version: aiFoundryAiServicesModelDeployment.version
- }
- raiPolicyName: aiFoundryAiServicesModelDeployment.raiPolicyName
- sku: {
- name: aiFoundryAiServicesModelDeployment.sku.name
- capacity: aiFoundryAiServicesModelDeployment.sku.capacity
- }
- }
- ]
- }
-}
-
-// AI Foundry: AI Project
-// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai
-var existingAiFounryProjectName = useExistingFoundryProject ? last(split( existingFoundryProjectResourceId,'/')) : ''
-var aiFoundryAiProjectName = useExistingFoundryProject ? existingAiFounryProjectName : aiFoundryAiProjectConfiguration.?name ?? 'aifp-${solutionPrefix}'
-
-var useExistingResourceId = !empty(existingFoundryProjectResourceId)
-
-module cogServiceRoleAssignmentsNew './modules/role.bicep' = if(!useExistingResourceId) {
- params: {
- name: 'new-${guid(containerApp.name, aiFoundryAiServices.outputs.resourceId)}'
- principalId: containerApp.outputs.?systemAssignedMIPrincipalId!
- aiServiceName: aiFoundryAiServices.outputs.name
- }
- scope: resourceGroup(subscription().subscriptionId, resourceGroup().name)
-}
-
-module cogServiceRoleAssignmentsExisting './modules/role.bicep' = if(useExistingResourceId) {
- params: {
- name: 'reuse-${guid(containerApp.name, aiFoundryAiServices.outputs.aiProjectInfo.resourceId)}'
- principalId: containerApp.outputs.?systemAssignedMIPrincipalId!
- aiServiceName: aiFoundryAiServices.outputs.name
- }
- scope: resourceGroup( split(existingFoundryProjectResourceId, '/')[2], split(existingFoundryProjectResourceId, '/')[4])
-}
-
-// ========== Cosmos DB ========== //
-// WAF best practices for Cosmos DB: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/cosmos-db
-module privateDnsZonesCosmosDb 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (virtualNetworkEnabled) {
- name: take('avm.res.network.private-dns-zone.cosmos-db.${solutionPrefix}', 64)
- params: {
- name: 'privatelink.documents.azure.com'
- enableTelemetry: enableTelemetry
- virtualNetworkLinks: [
- {
- name: 'vnetlink-cosmosdb'
- virtualNetworkResourceId: virtualNetwork.outputs.resourceId
- }
- ]
- tags: tags
- }
-}
-
-var cosmosDbAccountEnabled = cosmosDbAccountConfiguration.?enabled ?? true
-var cosmosDbResourceName = cosmosDbAccountConfiguration.?name ?? 'cosmos-${solutionPrefix}'
-var cosmosDbDatabaseName = 'macae'
-var cosmosDbDatabaseMemoryContainerName = 'memory'
-module cosmosDb 'br/public:avm/res/document-db/database-account:0.12.0' = if (cosmosDbAccountEnabled) {
- name: take('avm.res.document-db.database-account.${cosmosDbResourceName}', 64)
- params: {
- // Required parameters
- name: cosmosDbAccountConfiguration.?name ?? 'cosmos-${solutionPrefix}'
- location: cosmosDbAccountConfiguration.?location ?? solutionLocation
- tags: cosmosDbAccountConfiguration.?tags ?? tags
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- databaseAccountOfferType: 'Standard'
- enableFreeTier: false
- networkRestrictions: {
- networkAclBypass: 'None'
- publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled'
- }
- privateEndpoints: virtualNetworkEnabled
- ? [
- {
- name: 'pep-${cosmosDbResourceName}'
- customNetworkInterfaceName: 'nic-${cosmosDbResourceName}'
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [{ privateDnsZoneResourceId: privateDnsZonesCosmosDb.outputs.resourceId }]
- }
- service: 'Sql'
- subnetResourceId: cosmosDbAccountConfiguration.?subnetResourceId ?? virtualNetwork.outputs.subnetResourceIds[0]
- }
- ]
- : []
- sqlDatabases: concat(cosmosDbAccountConfiguration.?sqlDatabases ?? [], [
- {
- name: cosmosDbDatabaseName
- containers: [
- {
- name: cosmosDbDatabaseMemoryContainerName
- paths: [
- '/session_id'
- ]
- kind: 'Hash'
- version: 2
- }
- ]
- }
- ])
- locations: [
- {
- locationName: cosmosDbAccountConfiguration.?location ?? solutionLocation
- failoverPriority: 0
- isZoneRedundant: false
- }
- ]
- capabilitiesToAdd: [
- 'EnableServerless'
- ]
- sqlRoleAssignmentsPrincipalIds: [
- containerApp.outputs.?systemAssignedMIPrincipalId
- ]
- sqlRoleDefinitions: [
- {
- // Replace this with built-in role definition Cosmos DB Built-in Data Contributor: https://docs.azure.cn/en-us/cosmos-db/nosql/security/reference-data-plane-roles#cosmos-db-built-in-data-contributor
- roleType: 'CustomRole'
- roleName: 'Cosmos DB SQL Data Contributor'
- name: 'cosmos-db-sql-data-contributor'
- dataAction: [
- 'Microsoft.DocumentDB/databaseAccounts/readMetadata'
- 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*'
- 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
- ]
- }
- ]
- }
-}
-
-// ========== Backend Container App Environment ========== //
-// WAF best practices for container apps: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-container-apps
-var containerAppEnvironmentEnabled = containerAppEnvironmentConfiguration.?enabled ?? true
-var containerAppEnvironmentResourceName = containerAppEnvironmentConfiguration.?name ?? 'cae-${solutionPrefix}'
-module containerAppEnvironment 'modules/container-app-environment.bicep' = if (containerAppEnvironmentEnabled) {
- name: take('module.container-app-environment.${containerAppEnvironmentResourceName}', 64)
- params: {
- name: containerAppEnvironmentResourceName
- tags: containerAppEnvironmentConfiguration.?tags ?? tags
- location: containerAppEnvironmentConfiguration.?location ?? solutionLocation
- logAnalyticsResourceId: logAnalyticsWorkspaceId
- publicNetworkAccess: 'Enabled'
- zoneRedundant: false
- applicationInsightsConnectionString: applicationInsights.outputs.connectionString
- enableTelemetry: enableTelemetry
- subnetResourceId: virtualNetworkEnabled
- ? containerAppEnvironmentConfiguration.?subnetResourceId ?? virtualNetwork.?outputs.?subnetResourceIds[3] ?? ''
- : ''
- }
-}
-
-// ========== Backend Container App Service ========== //
-// WAF best practices for container apps: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-container-apps
-var containerAppEnabled = containerAppConfiguration.?enabled ?? true
-var containerAppResourceName = containerAppConfiguration.?name ?? 'ca-${solutionPrefix}'
-module containerApp 'br/public:avm/res/app/container-app:0.14.2' = if (containerAppEnabled) {
- name: take('avm.res.app.container-app.${containerAppResourceName}', 64)
- params: {
- name: containerAppResourceName
- tags: containerAppConfiguration.?tags ?? tags
- location: containerAppConfiguration.?location ?? solutionLocation
- enableTelemetry: enableTelemetry
- environmentResourceId: containerAppConfiguration.?environmentResourceId ?? containerAppEnvironment.outputs.resourceId
- managedIdentities: {
- systemAssigned: true //Replace with user assigned identity
- userAssignedResourceIds: [userAssignedIdentity.outputs.resourceId]
- }
- ingressTargetPort: containerAppConfiguration.?ingressTargetPort ?? 8000
- ingressExternal: true
- activeRevisionsMode: 'Single'
- corsPolicy: {
- allowedOrigins: [
- 'https://${webSiteName}.azurewebsites.net'
- 'http://${webSiteName}.azurewebsites.net'
- ]
- }
- scaleSettings: {
- //TODO: Make maxReplicas and minReplicas parameterized
- maxReplicas: containerAppConfiguration.?maxReplicas ?? 1
- minReplicas: containerAppConfiguration.?minReplicas ?? 1
- rules: [
- {
- name: 'http-scaler'
- http: {
- metadata: {
- concurrentRequests: containerAppConfiguration.?concurrentRequests ?? '100'
- }
- }
- }
- ]
- }
- containers: [
- {
- name: containerAppConfiguration.?containerName ?? 'backend'
- image: '${containerAppConfiguration.?containerImageRegistryDomain ?? 'biabcontainerreg.azurecr.io'}/${containerAppConfiguration.?containerImageName ?? 'macaebackend'}:${containerAppConfiguration.?containerImageTag ?? 'latest'}'
- resources: {
- //TODO: Make cpu and memory parameterized
- cpu: containerAppConfiguration.?containerCpu ?? '2.0'
- memory: containerAppConfiguration.?containerMemory ?? '4.0Gi'
- }
- env: [
- {
- name: 'COSMOSDB_ENDPOINT'
- value: 'https://${cosmosDbResourceName}.documents.azure.com:443/'
- }
- {
- name: 'COSMOSDB_DATABASE'
- value: cosmosDbDatabaseName
- }
- {
- name: 'COSMOSDB_CONTAINER'
- value: cosmosDbDatabaseMemoryContainerName
- }
- {
- name: 'AZURE_OPENAI_ENDPOINT'
- value: 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
- }
- {
- name: 'AZURE_OPENAI_MODEL_NAME'
- value: aiFoundryAiServicesModelDeployment.name
- }
- {
- name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
- value: aiFoundryAiServicesModelDeployment.name
- }
- {
- name: 'AZURE_OPENAI_API_VERSION'
- value: '2025-01-01-preview' //TODO: set parameter/variable
- }
- {
- name: 'APPLICATIONINSIGHTS_INSTRUMENTATION_KEY'
- value: applicationInsights.outputs.instrumentationKey
- }
- {
- name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
- value: applicationInsights.outputs.connectionString
- }
- {
- name: 'AZURE_AI_SUBSCRIPTION_ID'
- value: subscription().subscriptionId
- }
- {
- name: 'AZURE_AI_RESOURCE_GROUP'
- value: resourceGroup().name
- }
- {
- name: 'AZURE_AI_PROJECT_NAME'
- value: aiFoundryAiProjectName
- }
- {
- name: 'FRONTEND_SITE_NAME'
- value: 'https://${webSiteName}.azurewebsites.net'
- }
- {
- name: 'AZURE_AI_AGENT_ENDPOINT'
- value: aiFoundryAiServices.outputs.aiProjectInfo.apiEndpoint
- }
- {
- name: 'AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME'
- value: aiFoundryAiServicesModelDeployment.name
- }
- {
- name: 'APP_ENV'
- value: 'Prod'
- }
- ]
- }
- ]
- }
-}
-
-var webServerFarmEnabled = webServerFarmConfiguration.?enabled ?? true
-var webServerFarmResourceName = webServerFarmConfiguration.?name ?? 'asp-${solutionPrefix}'
-
-// ========== Frontend server farm ========== //
-// WAF best practices for web app service: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/app-service-web-apps
-module webServerFarm 'br/public:avm/res/web/serverfarm:0.4.1' = if (webServerFarmEnabled) {
- name: take('avm.res.web.serverfarm.${webServerFarmResourceName}', 64)
- params: {
- name: webServerFarmResourceName
- tags: tags
- location: webServerFarmConfiguration.?location ?? solutionLocation
- skuName: webServerFarmConfiguration.?skuName ?? 'P1v3'
- skuCapacity: webServerFarmConfiguration.?skuCapacity ?? 3
- reserved: true
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- kind: 'linux'
- zoneRedundant: false //TODO: make it zone redundant for waf aligned
- }
-}
-
-// ========== Frontend web site ========== //
-// WAF best practices for web app service: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/app-service-web-apps
-var webSiteEnabled = webSiteConfiguration.?enabled ?? true
-
-var webSiteName = 'app-${solutionPrefix}'
-module webSite 'br/public:avm/res/web/site:0.15.1' = if (webSiteEnabled) {
- name: take('avm.res.web.site.${webSiteName}', 64)
- params: {
- name: webSiteName
- tags: webSiteConfiguration.?tags ?? tags
- location: webSiteConfiguration.?location ?? solutionLocation
- kind: 'app,linux,container'
- enableTelemetry: enableTelemetry
- serverFarmResourceId: webSiteConfiguration.?environmentResourceId ?? webServerFarm.?outputs.resourceId
- appInsightResourceId: applicationInsights.outputs.resourceId
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }]
- publicNetworkAccess: 'Enabled' //TODO: use Azure Front Door WAF or Application Gateway WAF instead
- siteConfig: {
- linuxFxVersion: 'DOCKER|${webSiteConfiguration.?containerImageRegistryDomain ?? 'biabcontainerreg.azurecr.io'}/${webSiteConfiguration.?containerImageName ?? 'macaefrontend'}:${webSiteConfiguration.?containerImageTag ?? 'latest'}'
- }
- appSettingsKeyValuePairs: {
- SCM_DO_BUILD_DURING_DEPLOYMENT: 'true'
- DOCKER_REGISTRY_SERVER_URL: 'https://${webSiteConfiguration.?containerImageRegistryDomain ?? 'biabcontainerreg.azurecr.io'}'
- WEBSITES_PORT: '3000'
- WEBSITES_CONTAINER_START_TIME_LIMIT: '1800' // 30 minutes, adjust as needed
- BACKEND_API_URL: 'https://${containerApp.outputs.fqdn}'
- AUTH_ENABLED: 'false'
- APP_ENV: 'Prod'
- }
- }
-}
-
-// ============ //
-// Outputs //
-// ============ //
-
-// Add your outputs here
-
-@description('The default url of the website to connect to the Multi-Agent Custom Automation Engine solution.')
-output webSiteDefaultHostname string = webSite.outputs.defaultHostname
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Log Analytics Workspace resource configuration.')
-type logAnalyticsWorkspaceConfigurationType = {
- @description('Optional. If the Log Analytics Workspace resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Log Analytics Workspace resource.')
- @maxLength(63)
- name: string?
-
- @description('Optional. Location for the Log Analytics Workspace resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to for the Log Analytics Workspace resource.')
- tags: object?
-
- @description('Optional. The SKU for the Log Analytics Workspace resource.')
- sku: ('CapacityReservation' | 'Free' | 'LACluster' | 'PerGB2018' | 'PerNode' | 'Premium' | 'Standalone' | 'Standard')?
-
- @description('Optional. The number of days to retain the data in the Log Analytics Workspace. If empty, it will be set to 365 days.')
- @maxValue(730)
- dataRetentionInDays: int?
-
- @description('Optional: Existing Log Analytics Workspace Resource ID')
- existingWorkspaceResourceId: string?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Application Insights resource configuration.')
-type applicationInsightsConfigurationType = {
- @description('Optional. If the Application Insights resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Application Insights resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Application Insights resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Application Insights resource.')
- tags: object?
-
- @description('Optional. The retention of Application Insights data in days. If empty, Standard will be used.')
- retentionInDays: (120 | 180 | 270 | 30 | 365 | 550 | 60 | 730 | 90)?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Application User Assigned Managed Identity resource configuration.')
-type userAssignedManagedIdentityType = {
- @description('Optional. If the User Assigned Managed Identity resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the User Assigned Managed Identity resource.')
- @maxLength(128)
- name: string?
-
- @description('Optional. Location for the User Assigned Managed Identity resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the User Assigned Managed Identity resource.')
- tags: object?
-}
-
-@export()
-import { securityRuleType } from 'br/public:avm/res/network/network-security-group:0.5.1'
-@description('The type for the Multi-Agent Custom Automation Engine Network Security Group resource configuration.')
-type networkSecurityGroupConfigurationType = {
- @description('Optional. If the Network Security Group resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Network Security Group resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Network Security Group resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Network Security Group resource.')
- tags: object?
-
- @description('Optional. The security rules to set for the Network Security Group resource.')
- securityRules: securityRuleType[]?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation virtual network resource configuration.')
-type virtualNetworkConfigurationType = {
- @description('Optional. If the Virtual Network resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Virtual Network resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Virtual Network resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Virtual Network resource.')
- tags: object?
-
- @description('Optional. An array of 1 or more IP Addresses prefixes for the Virtual Network resource.')
- addressPrefixes: string[]?
-
- @description('Optional. An array of 1 or more subnets for the Virtual Network resource.')
- subnets: subnetType[]?
-}
-
-import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-type subnetType = {
- @description('Optional. The Name of the subnet resource.')
- name: string
-
- @description('Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty.')
- addressPrefix: string?
-
- @description('Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty.')
- addressPrefixes: string[]?
-
- @description('Optional. Application gateway IP configurations of virtual network resource.')
- applicationGatewayIPConfigurations: object[]?
-
- @description('Optional. The delegation to enable on the subnet.')
- delegation: string?
-
- @description('Optional. The resource ID of the NAT Gateway to use for the subnet.')
- natGatewayResourceId: string?
-
- @description('Optional. The resource ID of the network security group to assign to the subnet.')
- networkSecurityGroupResourceId: string?
-
- @description('Optional. enable or disable apply network policies on private endpoint in the subnet.')
- privateEndpointNetworkPolicies: ('Disabled' | 'Enabled' | 'NetworkSecurityGroupEnabled' | 'RouteTableEnabled')?
-
- @description('Optional. enable or disable apply network policies on private link service in the subnet.')
- privateLinkServiceNetworkPolicies: ('Disabled' | 'Enabled')?
-
- @description('Optional. Array of role assignments to create.')
- roleAssignments: roleAssignmentType[]?
-
- @description('Optional. The resource ID of the route table to assign to the subnet.')
- routeTableResourceId: string?
-
- @description('Optional. An array of service endpoint policies.')
- serviceEndpointPolicies: object[]?
-
- @description('Optional. The service endpoints to enable on the subnet.')
- serviceEndpoints: string[]?
-
- @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.')
- defaultOutboundAccess: bool?
-
- @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.')
- sharingScope: ('DelegatedServices' | 'Tenant')?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Bastion resource configuration.')
-type bastionConfigurationType = {
- @description('Optional. If the Bastion resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Bastion resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Bastion resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Bastion resource.')
- tags: object?
-
- @description('Optional. The SKU for the Bastion resource.')
- sku: ('Basic' | 'Developer' | 'Premium' | 'Standard')?
-
- @description('Optional. The Virtual Network resource id where the Bastion resource should be deployed.')
- virtualNetworkResourceId: string?
-
- @description('Optional. The name of the Public Ip resource created to connect to Bastion.')
- publicIpResourceName: string?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine virtual machine resource configuration.')
-type virtualMachineConfigurationType = {
- @description('Optional. If the Virtual Machine resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Virtual Machine resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the Virtual Machine resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Virtual Machine resource.')
- tags: object?
-
- @description('Optional. Specifies the size for the Virtual Machine resource.')
- vmSize: (
- | 'Basic_A0'
- | 'Basic_A1'
- | 'Basic_A2'
- | 'Basic_A3'
- | 'Basic_A4'
- | 'Standard_A0'
- | 'Standard_A1'
- | 'Standard_A2'
- | 'Standard_A3'
- | 'Standard_A4'
- | 'Standard_A5'
- | 'Standard_A6'
- | 'Standard_A7'
- | 'Standard_A8'
- | 'Standard_A9'
- | 'Standard_A10'
- | 'Standard_A11'
- | 'Standard_A1_v2'
- | 'Standard_A2_v2'
- | 'Standard_A4_v2'
- | 'Standard_A8_v2'
- | 'Standard_A2m_v2'
- | 'Standard_A4m_v2'
- | 'Standard_A8m_v2'
- | 'Standard_B1s'
- | 'Standard_B1ms'
- | 'Standard_B2s'
- | 'Standard_B2ms'
- | 'Standard_B4ms'
- | 'Standard_B8ms'
- | 'Standard_D1'
- | 'Standard_D2'
- | 'Standard_D3'
- | 'Standard_D4'
- | 'Standard_D11'
- | 'Standard_D12'
- | 'Standard_D13'
- | 'Standard_D14'
- | 'Standard_D1_v2'
- | 'Standard_D2_v2'
- | 'Standard_D3_v2'
- | 'Standard_D4_v2'
- | 'Standard_D5_v2'
- | 'Standard_D2_v3'
- | 'Standard_D4_v3'
- | 'Standard_D8_v3'
- | 'Standard_D16_v3'
- | 'Standard_D32_v3'
- | 'Standard_D64_v3'
- | 'Standard_D2s_v3'
- | 'Standard_D4s_v3'
- | 'Standard_D8s_v3'
- | 'Standard_D16s_v3'
- | 'Standard_D32s_v3'
- | 'Standard_D64s_v3'
- | 'Standard_D11_v2'
- | 'Standard_D12_v2'
- | 'Standard_D13_v2'
- | 'Standard_D14_v2'
- | 'Standard_D15_v2'
- | 'Standard_DS1'
- | 'Standard_DS2'
- | 'Standard_DS3'
- | 'Standard_DS4'
- | 'Standard_DS11'
- | 'Standard_DS12'
- | 'Standard_DS13'
- | 'Standard_DS14'
- | 'Standard_DS1_v2'
- | 'Standard_DS2_v2'
- | 'Standard_DS3_v2'
- | 'Standard_DS4_v2'
- | 'Standard_DS5_v2'
- | 'Standard_DS11_v2'
- | 'Standard_DS12_v2'
- | 'Standard_DS13_v2'
- | 'Standard_DS14_v2'
- | 'Standard_DS15_v2'
- | 'Standard_DS13-4_v2'
- | 'Standard_DS13-2_v2'
- | 'Standard_DS14-8_v2'
- | 'Standard_DS14-4_v2'
- | 'Standard_E2_v3'
- | 'Standard_E4_v3'
- | 'Standard_E8_v3'
- | 'Standard_E16_v3'
- | 'Standard_E32_v3'
- | 'Standard_E64_v3'
- | 'Standard_E2s_v3'
- | 'Standard_E4s_v3'
- | 'Standard_E8s_v3'
- | 'Standard_E16s_v3'
- | 'Standard_E32s_v3'
- | 'Standard_E64s_v3'
- | 'Standard_E32-16_v3'
- | 'Standard_E32-8s_v3'
- | 'Standard_E64-32s_v3'
- | 'Standard_E64-16s_v3'
- | 'Standard_F1'
- | 'Standard_F2'
- | 'Standard_F4'
- | 'Standard_F8'
- | 'Standard_F16'
- | 'Standard_F1s'
- | 'Standard_F2s'
- | 'Standard_F4s'
- | 'Standard_F8s'
- | 'Standard_F16s'
- | 'Standard_F2s_v2'
- | 'Standard_F4s_v2'
- | 'Standard_F8s_v2'
- | 'Standard_F16s_v2'
- | 'Standard_F32s_v2'
- | 'Standard_F64s_v2'
- | 'Standard_F72s_v2'
- | 'Standard_G1'
- | 'Standard_G2'
- | 'Standard_G3'
- | 'Standard_G4'
- | 'Standard_G5'
- | 'Standard_GS1'
- | 'Standard_GS2'
- | 'Standard_GS3'
- | 'Standard_GS4'
- | 'Standard_GS5'
- | 'Standard_GS4-8'
- | 'Standard_GS4-4'
- | 'Standard_GS5-16'
- | 'Standard_GS5-8'
- | 'Standard_H8'
- | 'Standard_H16'
- | 'Standard_H8m'
- | 'Standard_H16m'
- | 'Standard_H16r'
- | 'Standard_H16mr'
- | 'Standard_L4s'
- | 'Standard_L8s'
- | 'Standard_L16s'
- | 'Standard_L32s'
- | 'Standard_M64s'
- | 'Standard_M64ms'
- | 'Standard_M128s'
- | 'Standard_M128ms'
- | 'Standard_M64-32ms'
- | 'Standard_M64-16ms'
- | 'Standard_M128-64ms'
- | 'Standard_M128-32ms'
- | 'Standard_NC6'
- | 'Standard_NC12'
- | 'Standard_NC24'
- | 'Standard_NC24r'
- | 'Standard_NC6s_v2'
- | 'Standard_NC12s_v2'
- | 'Standard_NC24s_v2'
- | 'Standard_NC24rs_v2'
- | 'Standard_NC6s_v3'
- | 'Standard_NC12s_v3'
- | 'Standard_NC24s_v3'
- | 'Standard_NC24rs_v3'
- | 'Standard_ND6s'
- | 'Standard_ND12s'
- | 'Standard_ND24s'
- | 'Standard_ND24rs'
- | 'Standard_NV6'
- | 'Standard_NV12'
- | 'Standard_NV24')?
-
- @description('Optional. The username for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module.')
- adminUsername: string?
-
- @description('Optional. The password for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module.')
- @secure()
- adminPassword: string?
-
- @description('Optional. The resource ID of the subnet where the Virtual Machine resource should be deployed.')
- subnetResourceId: string?
-}
-
-@export()
-import { deploymentType } from 'br/public:avm/res/cognitive-services/account:0.10.2'
-@description('The type for the Multi-Agent Custom Automation Engine AI Services resource configuration.')
-type aiServicesConfigurationType = {
- @description('Optional. If the AI Services resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the AI Services resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the AI Services resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the AI Services resource.')
- tags: object?
-
- @description('Optional. The SKU of the AI Services resource. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.')
- sku: (
- | 'C2'
- | 'C3'
- | 'C4'
- | 'F0'
- | 'F1'
- | 'S'
- | 'S0'
- | 'S1'
- | 'S10'
- | 'S2'
- | 'S3'
- | 'S4'
- | 'S5'
- | 'S6'
- | 'S7'
- | 'S8'
- | 'S9')?
-
- @description('Optional. The resource Id of the subnet where the AI Services private endpoint should be created.')
- subnetResourceId: string?
-
- @description('Optional. The model deployments to set for the AI Services resource.')
- deployments: deploymentType[]?
-
- @description('Optional. The capacity to set for AI Services GTP model.')
- modelCapacity: int?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine AI Foundry AI Project resource configuration.')
-type aiProjectConfigurationType = {
- @description('Optional. If the AI Project resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the AI Project resource.')
- @maxLength(90)
- name: string?
-
- @description('Optional. Location for the AI Project resource deployment.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The SKU of the AI Project resource.')
- sku: ('Basic' | 'Free' | 'Standard' | 'Premium')?
-
- @description('Optional. The tags to set for the AI Project resource.')
- tags: object?
-}
-
-import { sqlDatabaseType } from 'br/public:avm/res/document-db/database-account:0.13.0'
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Cosmos DB Account resource configuration.')
-type cosmosDbAccountConfigurationType = {
- @description('Optional. If the Cosmos DB Account resource should be deployed or not.')
- enabled: bool?
- @description('Optional. The name of the Cosmos DB Account resource.')
- @maxLength(60)
- name: string?
-
- @description('Optional. Location for the Cosmos DB Account resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Cosmos DB Account resource.')
- tags: object?
-
- @description('Optional. The resource Id of the subnet where the Cosmos DB Account private endpoint should be created.')
- subnetResourceId: string?
-
- @description('Optional. The SQL databases configuration for the Cosmos DB Account resource.')
- sqlDatabases: sqlDatabaseType[]?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Container App Environment resource configuration.')
-type containerAppEnvironmentConfigurationType = {
- @description('Optional. If the Container App Environment resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Container App Environment resource.')
- @maxLength(60)
- name: string?
-
- @description('Optional. Location for the Container App Environment resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Container App Environment resource.')
- tags: object?
-
- @description('Optional. The resource Id of the subnet where the Container App Environment private endpoint should be created.')
- subnetResourceId: string?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Container App resource configuration.')
-type containerAppConfigurationType = {
- @description('Optional. If the Container App resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Container App resource.')
- @maxLength(60)
- name: string?
-
- @description('Optional. Location for the Container App resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Container App resource.')
- tags: object?
-
- @description('Optional. The resource Id of the Container App Environment where the Container App should be created.')
- environmentResourceId: string?
-
- @description('Optional. The maximum number of replicas of the Container App.')
- maxReplicas: int?
-
- @description('Optional. The minimum number of replicas of the Container App.')
- minReplicas: int?
-
- @description('Optional. The ingress target port of the Container App.')
- ingressTargetPort: int?
-
- @description('Optional. The concurrent requests allowed for the Container App.')
- concurrentRequests: string?
-
- @description('Optional. The name given to the Container App.')
- containerName: string?
-
- @description('Optional. The container registry domain of the container image to be used by the Container App. Default to `biabcontainerreg.azurecr.io`')
- containerImageRegistryDomain: string?
-
- @description('Optional. The name of the container image to be used by the Container App.')
- containerImageName: string?
-
- @description('Optional. The tag of the container image to be used by the Container App.')
- containerImageTag: string?
-
- @description('Optional. The CPU reserved for the Container App. Defaults to 2.0')
- containerCpu: string?
-
- @description('Optional. The Memory reserved for the Container App. Defaults to 4.0Gi')
- containerMemory: string?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Entra ID Application resource configuration.')
-type entraIdApplicationConfigurationType = {
- @description('Optional. If the Entra ID Application for website authentication should be deployed or not.')
- enabled: bool?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Web Server Farm resource configuration.')
-type webServerFarmConfigurationType = {
- @description('Optional. If the Web Server Farm resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Web Server Farm resource.')
- @maxLength(60)
- name: string?
-
- @description('Optional. Location for the Web Server Farm resource.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Web Server Farm resource.')
- tags: object?
-
- @description('Optional. The name of th SKU that will determine the tier, size and family for the Web Server Farm resource. This defaults to P1v3 to leverage availability zones.')
- skuName: string?
-
- @description('Optional. Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones.')
- skuCapacity: int?
-}
-
-@export()
-@description('The type for the Multi-Agent Custom Automation Engine Web Site resource configuration.')
-type webSiteConfigurationType = {
- @description('Optional. If the Web Site resource should be deployed or not.')
- enabled: bool?
-
- @description('Optional. The name of the Web Site resource.')
- @maxLength(60)
- name: string?
-
- @description('Optional. Location for the Web Site resource deployment.')
- @metadata({ azd: { type: 'location' } })
- location: string?
-
- @description('Optional. The tags to set for the Web Site resource.')
- tags: object?
-
- @description('Optional. The resource Id of the Web Site Environment where the Web Site should be created.')
- environmentResourceId: string?
-
- @description('Optional. The name given to the Container App.')
- containerName: string?
-
- @description('Optional. The container registry domain of the container image to be used by the Web Site. Default to `biabcontainerreg.azurecr.io`')
- containerImageRegistryDomain: string?
-
- @description('Optional. The name of the container image to be used by the Web Site.')
- containerImageName: string?
-
- @description('Optional. The tag of the container image to be used by the Web Site.')
- containerImageTag: string?
-}
diff --git a/infra/old/08-2025/main.parameters.json b/infra/old/08-2025/main.parameters.json
deleted file mode 100644
index efab1db7f..000000000
--- a/infra/old/08-2025/main.parameters.json
+++ /dev/null
@@ -1,102 +0,0 @@
-{
- "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
- "contentVersion": "1.0.0.0",
- "parameters": {
- "aiModelDeployments": {
- "value": [
- {
- "name": "gpt",
- "model": {
- "name": "gpt-4o",
- "version": "2024-08-06",
- "format": "OpenAI"
- },
- "sku": {
- "name": "GlobalStandard",
- "capacity": 140
- }
- }
- ]
- },
- "environmentName": {
- "value": "${AZURE_ENV_NAME}"
- },
- "solutionLocation": {
- "value": "${AZURE_LOCATION}"
- },
- "aiDeploymentsLocation": {
- "value": "${AZURE_ENV_OPENAI_LOCATION}"
- },
- "modelDeploymentType": {
- "value": "${AZURE_ENV_MODEL_DEPLOYMENT_TYPE}"
- },
- "gptModelName": {
- "value": "${AZURE_ENV_MODEL_NAME}"
- },
- "gptModelVersion": {
- "value": "${AZURE_ENV_MODEL_VERSION}"
- },
- "gptModelCapacity": {
- "value": "${AZURE_ENV_MODEL_CAPACITY}"
- },
- "existingFoundryProjectResourceId": {
- "value": "${AZURE_EXISTING_AI_PROJECT_RESOURCE_ID}"
- },
- "imageTag": {
- "value": "${AZURE_ENV_IMAGE_TAG}"
- },
- "enableTelemetry": {
- "value": "${AZURE_ENV_ENABLE_TELEMETRY}"
- },
- "existingLogAnalyticsWorkspaceId": {
- "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
- },
- "backendExists": {
- "value": "${SERVICE_BACKEND_RESOURCE_EXISTS=false}"
- },
- "backendDefinition": {
- "value": {
- "settings": [
- {
- "name": "",
- "value": "${VAR}",
- "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
- "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment."
- },
- {
- "name": "",
- "value": "${VAR_S}",
- "secret": true,
- "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
- "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment."
- }
- ]
- }
- },
- "frontendExists": {
- "value": "${SERVICE_FRONTEND_RESOURCE_EXISTS=false}"
- },
- "frontendDefinition": {
- "value": {
- "settings": [
- {
- "name": "",
- "value": "${VAR}",
- "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
- "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment."
- },
- {
- "name": "",
- "value": "${VAR_S}",
- "secret": true,
- "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
- "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment."
- }
- ]
- }
- },
- "principalId": {
- "value": "${AZURE_PRINCIPAL_ID}"
- }
- }
-}
\ No newline at end of file
diff --git a/infra/old/08-2025/modules/account/main.bicep b/infra/old/08-2025/modules/account/main.bicep
deleted file mode 100644
index b1fad4456..000000000
--- a/infra/old/08-2025/modules/account/main.bicep
+++ /dev/null
@@ -1,421 +0,0 @@
-metadata name = 'Cognitive Services'
-metadata description = 'This module deploys a Cognitive Service.'
-
-@description('Required. The name of Cognitive Services account.')
-param name string
-
-@description('Optional: Name for the project which needs to be created.')
-param projectName string
-
-@description('Optional: Description for the project which needs to be created.')
-param projectDescription string
-
-param existingFoundryProjectResourceId string = ''
-
-@description('Required. Kind of the Cognitive Services account. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.')
-@allowed([
- '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'
-])
-param kind string
-
-@description('Optional. SKU of the Cognitive Services account. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.')
-@allowed([
- 'C2'
- 'C3'
- 'C4'
- 'F0'
- 'F1'
- 'S'
- 'S0'
- 'S1'
- 'S10'
- 'S2'
- 'S3'
- 'S4'
- 'S5'
- 'S6'
- 'S7'
- 'S8'
- 'S9'
-])
-param sku string = 'S0'
-
-@description('Optional. Location for all Resources.')
-param location string = resourceGroup().location
-
-import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. The diagnostic settings of the service.')
-param diagnosticSettings diagnosticSettingFullType[]?
-
-@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.')
-@allowed([
- 'Enabled'
- 'Disabled'
-])
-param publicNetworkAccess string?
-
-@description('Conditional. Subdomain name used for token-based authentication. Required if \'networkAcls\' or \'privateEndpoints\' are set.')
-param customSubDomainName string?
-
-@description('Optional. A collection of rules governing the accessibility from specific network locations.')
-param networkAcls object?
-
-import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.')
-param privateEndpoints privateEndpointSingleServiceType[]?
-
-import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. The lock settings of the service.')
-param lock lockType?
-
-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[]?
-
-@description('Optional. Tags of the resource.')
-param tags object?
-
-@description('Optional. List of allowed FQDN.')
-param allowedFqdnList array?
-
-@description('Optional. The API properties for special APIs.')
-param apiProperties object?
-
-@description('Optional. Allow only Azure AD authentication. Should be enabled for security reasons.')
-param disableLocalAuth bool = true
-
-import { customerManagedKeyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. The customer managed key definition.')
-param customerManagedKey customerManagedKeyType?
-
-@description('Optional. The flag to enable dynamic throttling.')
-param dynamicThrottlingEnabled bool = false
-
-@secure()
-@description('Optional. Resource migration token.')
-param migrationToken string?
-
-@description('Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists.')
-param restore bool = false
-
-@description('Optional. Restrict outbound network access.')
-param restrictOutboundNetworkAccess bool = true
-
-@description('Optional. The storage accounts for this resource.')
-param userOwnedStorage array?
-
-import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. The managed identity definition for this resource.')
-param managedIdentities managedIdentityAllType?
-
-@description('Optional. Enable/Disable usage telemetry for module.')
-param enableTelemetry bool = true
-
-@description('Optional. Array of deployments about cognitive service accounts to create.')
-param deployments deploymentType[]?
-
-@description('Optional. Key vault reference and secret settings for the module\'s secrets export.')
-param secretsExportConfiguration secretsExportConfigurationType?
-
-@description('Optional. Enable/Disable project management feature for AI Foundry.')
-param allowProjectManagement bool?
-
-var formattedUserAssignedIdentities = reduce(
- map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
- {},
- (cur, next) => union(cur, next)
-) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
-
-var identity = !empty(managedIdentities)
- ? {
- type: (managedIdentities.?systemAssigned ?? false)
- ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned')
- : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null)
- userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
- }
- : null
-
-#disable-next-line no-deployments-resources
-resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
- name: '46d3xbcp.res.cognitiveservices-account.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, 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'
- }
- }
- }
- }
-}
-
-resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) {
- name: last(split(customerManagedKey.?keyVaultResourceId!, '/'))
- scope: resourceGroup(
- split(customerManagedKey.?keyVaultResourceId!, '/')[2],
- split(customerManagedKey.?keyVaultResourceId!, '/')[4]
- )
-
- resource cMKKey 'keys@2023-07-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) {
- name: customerManagedKey.?keyName!
- }
-}
-
-resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) {
- name: last(split(customerManagedKey.?userAssignedIdentityResourceId!, '/'))
- scope: resourceGroup(
- split(customerManagedKey.?userAssignedIdentityResourceId!, '/')[2],
- split(customerManagedKey.?userAssignedIdentityResourceId!, '/')[4]
- )
-}
-
-var useExistingService = !empty(existingFoundryProjectResourceId)
-
-resource cognitiveServiceNew 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = if(!useExistingService) {
- name: name
- kind: kind
- identity: identity
- location: location
- tags: tags
- sku: {
- name: sku
- }
- properties: {
- allowProjectManagement: allowProjectManagement // allows project management for Cognitive Services accounts in AI Foundry - FDP updates
- customSubDomainName: customSubDomainName
- networkAcls: !empty(networkAcls ?? {})
- ? {
- defaultAction: networkAcls.?defaultAction
- virtualNetworkRules: networkAcls.?virtualNetworkRules ?? []
- ipRules: networkAcls.?ipRules ?? []
- }
- : null
- publicNetworkAccess: publicNetworkAccess != null
- ? publicNetworkAccess
- : (!empty(networkAcls) ? 'Enabled' : 'Disabled')
- allowedFqdnList: allowedFqdnList
- apiProperties: apiProperties
- disableLocalAuth: disableLocalAuth
- encryption: !empty(customerManagedKey)
- ? {
- keySource: 'Microsoft.KeyVault'
- keyVaultProperties: {
- identityClientId: !empty(customerManagedKey.?userAssignedIdentityResourceId ?? '')
- ? cMKUserAssignedIdentity.properties.clientId
- : null
- keyVaultUri: cMKKeyVault.properties.vaultUri
- keyName: customerManagedKey!.keyName
- keyVersion: !empty(customerManagedKey.?keyVersion ?? '')
- ? customerManagedKey!.?keyVersion
- : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/'))
- }
- }
- : null
- migrationToken: migrationToken
- restore: restore
- restrictOutboundNetworkAccess: restrictOutboundNetworkAccess
- userOwnedStorage: userOwnedStorage
- dynamicThrottlingEnabled: dynamicThrottlingEnabled
- }
-}
-
-var existingCognitiveServiceDetails = split(existingFoundryProjectResourceId, '/')
-
-resource cognitiveServiceExisting 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = if(useExistingService) {
- name: existingCognitiveServiceDetails[8]
- scope: resourceGroup(existingCognitiveServiceDetails[2], existingCognitiveServiceDetails[4])
-}
-
-module cognigive_service_dependencies 'modules/dependencies.bicep' = if(!useExistingService) {
- params: {
- projectName: projectName
- projectDescription: projectDescription
- name: cognitiveServiceNew.name
- location: location
- deployments: deployments
- diagnosticSettings: diagnosticSettings
- lock: lock
- privateEndpoints: privateEndpoints
- roleAssignments: roleAssignments
- secretsExportConfiguration: secretsExportConfiguration
- sku: sku
- tags: tags
- }
-}
-
-module existing_cognigive_service_dependencies 'modules/dependencies.bicep' = if(useExistingService) {
- params: {
- name: cognitiveServiceExisting.name
- projectName: projectName
- projectDescription: projectDescription
- azureExistingAIProjectResourceId: existingFoundryProjectResourceId
- location: location
- deployments: deployments
- diagnosticSettings: diagnosticSettings
- lock: lock
- privateEndpoints: privateEndpoints
- roleAssignments: roleAssignments
- secretsExportConfiguration: secretsExportConfiguration
- sku: sku
- tags: tags
- }
- scope: resourceGroup(existingCognitiveServiceDetails[2], existingCognitiveServiceDetails[4])
-}
-
-var cognitiveService = useExistingService ? cognitiveServiceExisting : cognitiveServiceNew
-
-@description('The name of the cognitive services account.')
-output name string = useExistingService ? cognitiveServiceExisting.name : cognitiveServiceNew.name
-
-@description('The resource ID of the cognitive services account.')
-output resourceId string = useExistingService ? cognitiveServiceExisting.id : cognitiveServiceNew.id
-
-@description('The resource group the cognitive services account was deployed into.')
-output subscriptionId string = useExistingService ? existingCognitiveServiceDetails[2] : subscription().subscriptionId
-
-@description('The resource group the cognitive services account was deployed into.')
-output resourceGroupName string = useExistingService ? existingCognitiveServiceDetails[4] : resourceGroup().name
-
-@description('The service endpoint of the cognitive services account.')
-output endpoint string = useExistingService ? cognitiveServiceExisting.properties.endpoint : cognitiveService.properties.endpoint
-
-@description('All endpoints available for the cognitive services account, types depends on the cognitive service kind.')
-output endpoints endpointType = useExistingService ? cognitiveServiceExisting.properties.endpoints : cognitiveService.properties.endpoints
-
-@description('The principal ID of the system assigned identity.')
-output systemAssignedMIPrincipalId string? = useExistingService ? cognitiveServiceExisting.identity.principalId : cognitiveService.?identity.?principalId
-
-@description('The location the resource was deployed into.')
-output location string = useExistingService ? cognitiveServiceExisting.location : cognitiveService.location
-
-import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
-output exportedSecrets secretsOutputType = useExistingService ? existing_cognigive_service_dependencies.outputs.exportedSecrets : cognigive_service_dependencies.outputs.exportedSecrets
-
-@description('The private endpoints of the congitive services account.')
-output privateEndpoints privateEndpointOutputType[] = useExistingService ? existing_cognigive_service_dependencies.outputs.privateEndpoints : cognigive_service_dependencies.outputs.privateEndpoints
-
-import { aiProjectOutputType } from './modules/project.bicep'
-output aiProjectInfo aiProjectOutputType = useExistingService ? existing_cognigive_service_dependencies.outputs.aiProjectInfo : cognigive_service_dependencies.outputs.aiProjectInfo
-
-// ================ //
-// Definitions //
-// ================ //
-
-@export()
-@description('The type for the private endpoint output.')
-type privateEndpointOutputType = {
- @description('The name of the private endpoint.')
- name: string
-
- @description('The resource ID of the private endpoint.')
- resourceId: string
-
- @description('The group Id for the private endpoint Group.')
- groupId: string?
-
- @description('The custom DNS configurations of the private endpoint.')
- customDnsConfigs: {
- @description('FQDN that resolves to private endpoint IP address.')
- fqdn: string?
-
- @description('A list of private IP addresses of the private endpoint.')
- ipAddresses: string[]
- }[]
-
- @description('The IDs of the network interfaces associated with the private endpoint.')
- networkInterfaceResourceIds: string[]
-}
-
-@export()
-@description('The type for a cognitive services account deployment.')
-type deploymentType = {
- @description('Optional. Specify the name of cognitive service account deployment.')
- name: string?
-
- @description('Required. Properties of Cognitive Services account deployment model.')
- model: {
- @description('Required. The name of Cognitive Services account deployment model.')
- name: string
-
- @description('Required. The format of Cognitive Services account deployment model.')
- format: string
-
- @description('Required. The version of Cognitive Services account deployment model.')
- version: string
- }
-
- @description('Optional. The resource model definition representing SKU.')
- sku: {
- @description('Required. The name of the resource model definition representing SKU.')
- name: string
-
- @description('Optional. The capacity of the resource model definition representing SKU.')
- capacity: int?
-
- @description('Optional. The tier of the resource model definition representing SKU.')
- tier: string?
-
- @description('Optional. The size of the resource model definition representing SKU.')
- size: string?
-
- @description('Optional. The family of the resource model definition representing SKU.')
- family: string?
- }?
-
- @description('Optional. The name of RAI policy.')
- raiPolicyName: string?
-
- @description('Optional. The version upgrade option.')
- versionUpgradeOption: string?
-}
-
-@export()
-@description('The type for a cognitive services account endpoint.')
-type endpointType = {
- @description('Type of the endpoint.')
- name: string?
- @description('The endpoint URI.')
- endpoint: string?
-}
-
-@export()
-@description('The type of the secrets exported to the provided Key Vault.')
-type secretsExportConfigurationType = {
- @description('Required. The key vault name where to store the keys and connection strings generated by the modules.')
- keyVaultResourceId: string
-
- @description('Optional. The name for the accessKey1 secret to create.')
- accessKey1Name: string?
-
- @description('Optional. The name for the accessKey2 secret to create.')
- accessKey2Name: string?
-}
diff --git a/infra/old/08-2025/modules/account/modules/dependencies.bicep b/infra/old/08-2025/modules/account/modules/dependencies.bicep
deleted file mode 100644
index c2d7de6f8..000000000
--- a/infra/old/08-2025/modules/account/modules/dependencies.bicep
+++ /dev/null
@@ -1,479 +0,0 @@
-@description('Required. The name of Cognitive Services account.')
-param name string
-
-@description('Optional. SKU of the Cognitive Services account. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.')
-@allowed([
- 'C2'
- 'C3'
- 'C4'
- 'F0'
- 'F1'
- 'S'
- 'S0'
- 'S1'
- 'S10'
- 'S2'
- 'S3'
- 'S4'
- 'S5'
- 'S6'
- 'S7'
- 'S8'
- 'S9'
-])
-param sku string = 'S0'
-
-@description('Optional. Location for all Resources.')
-param location string = resourceGroup().location
-
-@description('Optional. Tags of the resource.')
-param tags object?
-
-@description('Optional. Array of deployments about cognitive service accounts to create.')
-param deployments deploymentType[]?
-
-@description('Optional. Key vault reference and secret settings for the module\'s secrets export.')
-param secretsExportConfiguration secretsExportConfigurationType?
-
-import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.')
-param privateEndpoints privateEndpointSingleServiceType[]?
-
-import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. The lock settings of the service.')
-param lock lockType?
-
-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[]?
-
-import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Optional. The diagnostic settings of the service.')
-param diagnosticSettings diagnosticSettingFullType[]?
-
-@description('Optional: Name for the project which needs to be created.')
-param projectName string
-
-@description('Optional: Description for the project which needs to be created.')
-param projectDescription string
-
-@description('Optional: Provide the existing project resource id in case if it needs to be reused')
-param azureExistingAIProjectResourceId string = ''
-
-var 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'
- )
-}
-
-var formattedRoleAssignments = [
- for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, {
- roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains(
- roleAssignment.roleDefinitionIdOrName,
- '/providers/Microsoft.Authorization/roleDefinitions/'
- )
- ? roleAssignment.roleDefinitionIdOrName
- : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName))
- })
-]
-
-var enableReferencedModulesTelemetry = false
-
-resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = {
- name: name
-}
-
-@batchSize(1)
-resource cognitiveService_deployments 'Microsoft.CognitiveServices/accounts/deployments@2025-04-01-preview' = [
- for (deployment, index) in (deployments ?? []): {
- parent: cognitiveService
- name: deployment.?name ?? '${name}-deployments'
- properties: {
- model: deployment.model
- raiPolicyName: deployment.?raiPolicyName
- versionUpgradeOption: deployment.?versionUpgradeOption
- }
- sku: deployment.?sku ?? {
- name: sku
- capacity: sku.?capacity
- tier: sku.?tier
- size: sku.?size
- family: sku.?family
- }
- }
-]
-
-resource cognitiveService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
- name: lock.?name ?? 'lock-${name}'
- properties: {
- level: lock.?kind ?? ''
- notes: lock.?kind == 'CanNotDelete'
- ? 'Cannot delete resource or child resources.'
- : 'Cannot delete or modify the resource or child resources.'
- }
- scope: cognitiveService
-}
-
-resource cognitiveService_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [
- for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
- name: diagnosticSetting.?name ?? '${name}-diagnosticSettings'
- properties: {
- storageAccountId: diagnosticSetting.?storageAccountResourceId
- workspaceId: diagnosticSetting.?workspaceResourceId
- eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId
- eventHubName: diagnosticSetting.?eventHubName
- metrics: [
- for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): {
- category: group.category
- enabled: group.?enabled ?? true
- timeGrain: null
- }
- ]
- logs: [
- for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): {
- categoryGroup: group.?categoryGroup
- category: group.?category
- enabled: group.?enabled ?? true
- }
- ]
- marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId
- logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType
- }
- scope: cognitiveService
- }
-]
-
-module cognitiveService_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.11.0' = [
- for (privateEndpoint, index) in (privateEndpoints ?? []): {
- name: '${uniqueString(deployment().name, location)}-cognitiveService-PrivateEndpoint-${index}'
- scope: resourceGroup(
- split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2],
- split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4]
- )
- params: {
- name: privateEndpoint.?name ?? 'pep-${last(split(cognitiveService.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}'
- privateLinkServiceConnections: privateEndpoint.?isManualConnection != true
- ? [
- {
- name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(cognitiveService.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}'
- properties: {
- privateLinkServiceId: cognitiveService.id
- groupIds: [
- privateEndpoint.?service ?? 'account'
- ]
- }
- }
- ]
- : null
- manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true
- ? [
- {
- name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(cognitiveService.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}'
- properties: {
- privateLinkServiceId: cognitiveService.id
- groupIds: [
- privateEndpoint.?service ?? 'account'
- ]
- requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.'
- }
- }
- ]
- : null
- subnetResourceId: privateEndpoint.subnetResourceId
- enableTelemetry: enableReferencedModulesTelemetry
- location: privateEndpoint.?location ?? reference(
- split(privateEndpoint.subnetResourceId, '/subnets/')[0],
- '2020-06-01',
- 'Full'
- ).location
- lock: privateEndpoint.?lock ?? lock
- privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup
- roleAssignments: privateEndpoint.?roleAssignments
- tags: privateEndpoint.?tags ?? tags
- customDnsConfigs: privateEndpoint.?customDnsConfigs
- ipConfigurations: privateEndpoint.?ipConfigurations
- applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds
- customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName
- }
- }
-]
-
-resource cognitiveService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
- for (roleAssignment, index) in (formattedRoleAssignments ?? []): {
- name: roleAssignment.?name ?? guid(cognitiveService.id, roleAssignment.principalId, roleAssignment.roleDefinitionId)
- properties: {
- roleDefinitionId: roleAssignment.roleDefinitionId
- principalId: roleAssignment.principalId
- description: roleAssignment.?description
- principalType: roleAssignment.?principalType
- condition: roleAssignment.?condition
- conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
- delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
- }
- scope: cognitiveService
- }
-]
-
-module secretsExport './keyVaultExport.bicep' = if (secretsExportConfiguration != null) {
- name: '${uniqueString(deployment().name, location)}-secrets-kv'
- scope: resourceGroup(
- split(secretsExportConfiguration.?keyVaultResourceId!, '/')[2],
- split(secretsExportConfiguration.?keyVaultResourceId!, '/')[4]
- )
- params: {
- keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId!, '/'))
- secretsToSet: union(
- [],
- contains(secretsExportConfiguration!, 'accessKey1Name')
- ? [
- {
- name: secretsExportConfiguration!.?accessKey1Name
- value: cognitiveService.listKeys().key1
- }
- ]
- : [],
- contains(secretsExportConfiguration!, 'accessKey2Name')
- ? [
- {
- name: secretsExportConfiguration!.?accessKey2Name
- value: cognitiveService.listKeys().key2
- }
- ]
- : []
- )
- }
-}
-
-module aiProject 'project.bicep' = if(!empty(projectName) || !empty(azureExistingAIProjectResourceId)) {
- name: take('${name}-ai-project-${projectName}-deployment', 64)
- params: {
- name: projectName
- desc: projectDescription
- aiServicesName: cognitiveService.name
- location: location
- tags: tags
- azureExistingAIProjectResourceId: azureExistingAIProjectResourceId
- }
-}
-
-import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
-output exportedSecrets secretsOutputType = (secretsExportConfiguration != null)
- ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret)
- : {}
-
-@description('The private endpoints of the congitive services account.')
-output privateEndpoints privateEndpointOutputType[] = [
- for (pe, index) in (privateEndpoints ?? []): {
- name: cognitiveService_privateEndpoints[index].outputs.name
- resourceId: cognitiveService_privateEndpoints[index].outputs.resourceId
- groupId: cognitiveService_privateEndpoints[index].outputs.?groupId!
- customDnsConfigs: cognitiveService_privateEndpoints[index].outputs.customDnsConfigs
- networkInterfaceResourceIds: cognitiveService_privateEndpoints[index].outputs.networkInterfaceResourceIds
- }
-]
-
-import { aiProjectOutputType } from 'project.bicep'
-output aiProjectInfo aiProjectOutputType = aiProject.outputs.aiProjectInfo
-
-// ================ //
-// Definitions //
-// ================ //
-
-@export()
-@description('The type for the private endpoint output.')
-type privateEndpointOutputType = {
- @description('The name of the private endpoint.')
- name: string
-
- @description('The resource ID of the private endpoint.')
- resourceId: string
-
- @description('The group Id for the private endpoint Group.')
- groupId: string?
-
- @description('The custom DNS configurations of the private endpoint.')
- customDnsConfigs: {
- @description('FQDN that resolves to private endpoint IP address.')
- fqdn: string?
-
- @description('A list of private IP addresses of the private endpoint.')
- ipAddresses: string[]
- }[]
-
- @description('The IDs of the network interfaces associated with the private endpoint.')
- networkInterfaceResourceIds: string[]
-}
-
-@export()
-@description('The type for a cognitive services account deployment.')
-type deploymentType = {
- @description('Optional. Specify the name of cognitive service account deployment.')
- name: string?
-
- @description('Required. Properties of Cognitive Services account deployment model.')
- model: {
- @description('Required. The name of Cognitive Services account deployment model.')
- name: string
-
- @description('Required. The format of Cognitive Services account deployment model.')
- format: string
-
- @description('Required. The version of Cognitive Services account deployment model.')
- version: string
- }
-
- @description('Optional. The resource model definition representing SKU.')
- sku: {
- @description('Required. The name of the resource model definition representing SKU.')
- name: string
-
- @description('Optional. The capacity of the resource model definition representing SKU.')
- capacity: int?
-
- @description('Optional. The tier of the resource model definition representing SKU.')
- tier: string?
-
- @description('Optional. The size of the resource model definition representing SKU.')
- size: string?
-
- @description('Optional. The family of the resource model definition representing SKU.')
- family: string?
- }?
-
- @description('Optional. The name of RAI policy.')
- raiPolicyName: string?
-
- @description('Optional. The version upgrade option.')
- versionUpgradeOption: string?
-}
-
-@export()
-@description('The type for a cognitive services account endpoint.')
-type endpointType = {
- @description('Type of the endpoint.')
- name: string?
- @description('The endpoint URI.')
- endpoint: string?
-}
-
-@export()
-@description('The type of the secrets exported to the provided Key Vault.')
-type secretsExportConfigurationType = {
- @description('Required. The key vault name where to store the keys and connection strings generated by the modules.')
- keyVaultResourceId: string
-
- @description('Optional. The name for the accessKey1 secret to create.')
- accessKey1Name: string?
-
- @description('Optional. The name for the accessKey2 secret to create.')
- accessKey2Name: string?
-}
diff --git a/infra/old/08-2025/modules/account/modules/keyVaultExport.bicep b/infra/old/08-2025/modules/account/modules/keyVaultExport.bicep
deleted file mode 100644
index a54cc5576..000000000
--- a/infra/old/08-2025/modules/account/modules/keyVaultExport.bicep
+++ /dev/null
@@ -1,43 +0,0 @@
-// ============== //
-// Parameters //
-// ============== //
-
-@description('Required. The name of the Key Vault to set the ecrets in.')
-param keyVaultName string
-
-import { secretToSetType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('Required. The secrets to set in the Key Vault.')
-param secretsToSet secretToSetType[]
-
-// ============= //
-// Resources //
-// ============= //
-
-resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
- name: keyVaultName
-}
-
-resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [
- for secret in secretsToSet: {
- name: secret.name
- parent: keyVault
- properties: {
- value: secret.value
- }
- }
-]
-
-// =========== //
-// Outputs //
-// =========== //
-
-import { secretSetOutputType } from 'br/public:avm/utl/types/avm-common-types:0.5.1'
-@description('The references to the secrets exported to the provided Key Vault.')
-output secretsSet secretSetOutputType[] = [
- #disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value
- for index in range(0, length(secretsToSet ?? [])): {
- secretResourceId: secrets[index].id
- secretUri: secrets[index].properties.secretUri
- secretUriWithVersion: secrets[index].properties.secretUriWithVersion
- }
-]
diff --git a/infra/old/08-2025/modules/account/modules/project.bicep b/infra/old/08-2025/modules/account/modules/project.bicep
deleted file mode 100644
index 8ca346546..000000000
--- a/infra/old/08-2025/modules/account/modules/project.bicep
+++ /dev/null
@@ -1,61 +0,0 @@
-@description('Required. Name of the AI Services project.')
-param name string
-
-@description('Required. The location of the Project resource.')
-param location string = resourceGroup().location
-
-@description('Optional. The description of the AI Foundry project to create. Defaults to the project name.')
-param desc string = name
-
-@description('Required. Name of the existing Cognitive Services resource to create the AI Foundry project in.')
-param aiServicesName string
-
-@description('Optional. Tags to be applied to the resources.')
-param tags object = {}
-
-@description('Optional. Use this parameter to use an existing AI project resource ID from different resource group')
-param azureExistingAIProjectResourceId string = ''
-
-// // Extract components from existing AI Project Resource ID if provided
-var useExistingProject = !empty(azureExistingAIProjectResourceId)
-var existingProjName = useExistingProject ? last(split(azureExistingAIProjectResourceId, '/')) : ''
-var existingProjEndpoint = useExistingProject ? format('https://{0}.services.ai.azure.com/api/projects/{1}', aiServicesName, existingProjName) : ''
-// Reference to cognitive service in current resource group for new projects
-resource cogServiceReference 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = {
- name: aiServicesName
-}
-
-// Create new AI project only if not reusing existing one
-resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = if(!useExistingProject) {
- parent: cogServiceReference
- name: name
- tags: tags
- location: location
- identity: {
- type: 'SystemAssigned'
- }
- properties: {
- description: desc
- displayName: name
- }
-}
-
-@description('AI Project metadata including name, resource ID, and API endpoint.')
-output aiProjectInfo aiProjectOutputType = {
- name: useExistingProject ? existingProjName : aiProject.name
- resourceId: useExistingProject ? azureExistingAIProjectResourceId : aiProject.id
- apiEndpoint: useExistingProject ? existingProjEndpoint : aiProject.properties.endpoints['AI Foundry API']
-}
-
-@export()
-@description('Output type representing AI project information.')
-type aiProjectOutputType = {
- @description('Required. Name of the AI project.')
- name: string
-
- @description('Required. Resource ID of the AI project.')
- resourceId: string
-
- @description('Required. API endpoint for the AI project.')
- apiEndpoint: string
-}
diff --git a/infra/old/08-2025/modules/ai-hub.bicep b/infra/old/08-2025/modules/ai-hub.bicep
deleted file mode 100644
index c92acff92..000000000
--- a/infra/old/08-2025/modules/ai-hub.bicep
+++ /dev/null
@@ -1,62 +0,0 @@
-param name string
-param tags object
-param location string
-param sku string
-param storageAccountResourceId string
-param logAnalyticsWorkspaceResourceId string
-param applicationInsightsResourceId string
-param aiFoundryAiServicesName string
-param enableTelemetry bool
-param virtualNetworkEnabled bool
-import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.4.0'
-param privateEndpoints privateEndpointSingleServiceType[]
-
-resource aiServices 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = {
- name: aiFoundryAiServicesName
-}
-
-module aiFoundryAiHub 'br/public:avm/res/machine-learning-services/workspace:0.10.1' = {
- name: take('avm.res.machine-learning-services.workspace.${name}', 64)
- params: {
- name: name
- tags: tags
- location: location
- enableTelemetry: enableTelemetry
- diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }]
- kind: 'Hub'
- sku: sku
- description: 'AI Hub for Multi Agent Custom Automation Engine Solution Accelerator template'
- //associatedKeyVaultResourceId: keyVaultResourceId
- associatedStorageAccountResourceId: storageAccountResourceId
- associatedApplicationInsightsResourceId: applicationInsightsResourceId
- connections: [
- {
- name: 'connection-AzureOpenAI'
- category: 'AIServices'
- target: aiServices.properties.endpoint
- isSharedToAll: true
- metadata: {
- ApiType: 'Azure'
- ResourceId: aiServices.id
- }
- connectionProperties: {
- authType: 'ApiKey'
- credentials: {
- key: aiServices.listKeys().key1
- }
- }
- }
- ]
- //publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled'
- publicNetworkAccess: 'Enabled' //TODO: connection via private endpoint is not working from containers network. Change this when fixed
- managedNetworkSettings: virtualNetworkEnabled
- ? {
- isolationMode: 'AllowInternetOutbound'
- outboundRules: null //TODO: Refine this
- }
- : null
- privateEndpoints: privateEndpoints
- }
-}
-
-output resourceId string = aiFoundryAiHub.outputs.resourceId
diff --git a/infra/old/08-2025/modules/container-app-environment.bicep b/infra/old/08-2025/modules/container-app-environment.bicep
deleted file mode 100644
index 0fc2721f2..000000000
--- a/infra/old/08-2025/modules/container-app-environment.bicep
+++ /dev/null
@@ -1,93 +0,0 @@
-param name string
-param location string
-param logAnalyticsResourceId string
-param tags object
-param publicNetworkAccess string
-//param vnetConfiguration object
-param zoneRedundant bool
-//param aspireDashboardEnabled bool
-param enableTelemetry bool
-param subnetResourceId string
-param applicationInsightsConnectionString string
-
-var logAnalyticsSubscription = split(logAnalyticsResourceId, '/')[2]
-var logAnalyticsResourceGroup = split(logAnalyticsResourceId, '/')[4]
-var logAnalyticsName = split(logAnalyticsResourceId, '/')[8]
-
-resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-08-01' existing = {
- name: logAnalyticsName
- scope: resourceGroup(logAnalyticsSubscription, logAnalyticsResourceGroup)
-}
-
-// resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-08-02-preview' = {
-// name: name
-// location: location
-// tags: tags
-// properties: {
-// //daprAIConnectionString: appInsights.properties.ConnectionString
-// //daprAIConnectionString: applicationInsights.outputs.connectionString
-// appLogsConfiguration: {
-// destination: 'log-analytics'
-// logAnalyticsConfiguration: {
-// customerId: logAnalyticsWorkspace.properties.customerId
-// #disable-next-line use-secure-value-for-secure-inputs
-// sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
-// }
-// }
-// workloadProfiles: [
-// //THIS IS REQUIRED TO ADD PRIVATE ENDPOINTS
-// {
-// name: 'Consumption'
-// workloadProfileType: 'Consumption'
-// }
-// ]
-// publicNetworkAccess: publicNetworkAccess
-// vnetConfiguration: vnetConfiguration
-// zoneRedundant: zoneRedundant
-// }
-// }
-
-module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.1' = {
- name: take('avm.res.app.managed-environment.${name}', 64)
- params: {
- name: name
- location: location
- tags: tags
- enableTelemetry: enableTelemetry
- //daprAIConnectionString: applicationInsights.outputs.connectionString //Troubleshoot: ContainerAppsConfiguration.DaprAIConnectionString is invalid. DaprAIConnectionString can not be set when AppInsightsConfiguration has been set, please set DaprAIConnectionString to null. (Code:InvalidRequestParameterWithDetails
- appLogsConfiguration: {
- destination: 'log-analytics'
- logAnalyticsConfiguration: {
- customerId: logAnalyticsWorkspace.properties.customerId
- #disable-next-line use-secure-value-for-secure-inputs
- sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
- }
- }
- workloadProfiles: [
- //THIS IS REQUIRED TO ADD PRIVATE ENDPOINTS
- {
- name: 'Consumption'
- workloadProfileType: 'Consumption'
- }
- ]
- publicNetworkAccess: publicNetworkAccess
- appInsightsConnectionString: applicationInsightsConnectionString
- zoneRedundant: zoneRedundant
- infrastructureSubnetResourceId: subnetResourceId
- internal: false
- }
-}
-
-//TODO: FIX when deployed to vnet. This needs access to Azure to work
-// resource aspireDashboard 'Microsoft.App/managedEnvironments/dotNetComponents@2024-10-02-preview' = if (aspireDashboardEnabled) {
-// parent: containerAppEnvironment
-// name: 'aspire-dashboard'
-// properties: {
-// componentType: 'AspireDashboard'
-// }
-// }
-
-//output resourceId string = containerAppEnvironment.id
-output resourceId string = containerAppEnvironment.outputs.resourceId
-//output location string = containerAppEnvironment.location
-output location string = containerAppEnvironment.outputs.location
diff --git a/infra/old/08-2025/modules/fetch-container-image.bicep b/infra/old/08-2025/modules/fetch-container-image.bicep
deleted file mode 100644
index 78d1e7eeb..000000000
--- a/infra/old/08-2025/modules/fetch-container-image.bicep
+++ /dev/null
@@ -1,8 +0,0 @@
-param exists bool
-param name string
-
-resource existingApp 'Microsoft.App/containerApps@2023-05-02-preview' existing = if (exists) {
- name: name
-}
-
-output containers array = exists ? existingApp.properties.template.containers : []
diff --git a/infra/old/08-2025/modules/role.bicep b/infra/old/08-2025/modules/role.bicep
deleted file mode 100644
index cf8251635..000000000
--- a/infra/old/08-2025/modules/role.bicep
+++ /dev/null
@@ -1,58 +0,0 @@
-@description('The name of the role assignment resource. Typically generated using `guid()` for uniqueness.')
-param name string
-
-@description('The object ID of the principal (user, group, or service principal) to whom the role will be assigned.')
-param principalId string
-
-@description('The name of the existing Azure Cognitive Services account.')
-param aiServiceName string
-
-
-@allowed(['Device', 'ForeignGroup', 'Group', 'ServicePrincipal', 'User'])
-param principalType string = 'ServicePrincipal'
-
-resource cognitiveServiceExisting 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = {
- name: aiServiceName
-}
-
-resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
- name: '53ca6127-db72-4b80-b1b0-d745d6d5456d'
-}
-
-resource aiDeveloper 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
- name: '64702f94-c441-49e6-a78b-ef80e0188fee'
-}
-
-resource cognitiveServiceOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
- name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
-}
-
-resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(name, 'aiUserAccessFoundry')
- scope: cognitiveServiceExisting
- properties: {
- roleDefinitionId: aiUser.id
- principalId: principalId
- principalType: principalType
- }
-}
-
-resource aiDeveloperAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(name, 'aiDeveloperAccessFoundry')
- scope: cognitiveServiceExisting
- properties: {
- roleDefinitionId: aiDeveloper.id
- principalId: principalId
- principalType: principalType
- }
-}
-
-resource cognitiveServiceOpenAIUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- name: guid(name, 'cognitiveServiceOpenAIUserAccessFoundry')
- scope: cognitiveServiceExisting
- properties: {
- roleDefinitionId: cognitiveServiceOpenAIUser.id
- principalId: principalId
- principalType: principalType
- }
-}
diff --git a/infra/scripts/Process-Sample-Data.ps1 b/infra/scripts/Process-Sample-Data.ps1
deleted file mode 100644
index c6d67c817..000000000
--- a/infra/scripts/Process-Sample-Data.ps1
+++ /dev/null
@@ -1,256 +0,0 @@
-#Requires -Version 7.0
-
-param(
- [string]$StorageAccount,
- [string]$BlobContainer,
- [string]$AiSearch,
- [string]$AiSearchIndex,
- [string]$ResourceGroup,
- [string]$AzSubscriptionId
-)
-
-# Get parameters from azd env, if not provided
-if (-not $StorageAccount) {
- $StorageAccount = $(azd env get-value AZURE_STORAGE_ACCOUNT_NAME)
-}
-
-if (-not $BlobContainer) {
- $BlobContainer = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME)
-}
-
-if (-not $AiSearch) {
- $AiSearch = $(azd env get-value AZURE_AI_SEARCH_NAME)
-}
-
-if (-not $AiSearchIndex) {
- $AiSearchIndex = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME)
-}
-
-if (-not $ResourceGroup) {
- $ResourceGroup = $(azd env get-value AZURE_RESOURCE_GROUP)
-}
-
-if (-not $AzSubscriptionId) {
- $AzSubscriptionId = $(azd env get-value AZURE_SUBSCRIPTION_ID)
-}
-
-# Check if all required arguments are provided
-if (-not $StorageAccount -or -not $BlobContainer -or -not $AiSearch) {
- Write-Host "Usage: .\infra\scripts\Process-Sample-Data.ps1 -StorageAccount -BlobContainer -AiSearch [-AiSearchIndex ] [-ResourceGroup ]"
- exit 1
-}
-
-# Authenticate with Azure
-try {
- $currentAzContext = az account show | ConvertFrom-Json -ErrorAction Stop
- Write-Host "Already authenticated with Azure."
-}
-catch {
- Write-Host "Not authenticated with Azure. Attempting to authenticate..."
- Write-Host "Authenticating with Azure CLI..."
- az login
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Authentication failed."
- exit 1
- }
- $currentAzContext = az account show | ConvertFrom-Json
-}
-
-# Check if user has selected the correct subscription
-$currentSubscriptionId = $currentAzContext.id
-$currentSubscriptionName = $currentAzContext.name
-
-if ($currentSubscriptionId -ne $AzSubscriptionId) {
- Write-Host "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
- $confirmation = Read-Host "Do you want to continue with this subscription? (y/n)"
-
- if ($confirmation.ToLower() -ne "y") {
- Write-Host "Fetching available subscriptions..."
- $availableSubscriptions = (az account list --query "[?state=='Enabled']" | ConvertFrom-Json -AsHashtable)
-
- # Create a cleaner array of subscription objects
- $subscriptionArray = $availableSubscriptions | ForEach-Object {
- [PSCustomObject]@{
- Name = $_.name
- Id = $_.id
- }
- }
-
- do {
- Write-Host ""
- Write-Host "Available Subscriptions:"
- Write-Host "========================"
- for ($i = 0; $i -lt $subscriptionArray.Count; $i++) {
- Write-Host "$($i+1). $($subscriptionArray[$i].Name) ( $($subscriptionArray[$i].Id) )"
- }
- Write-Host "========================"
- Write-Host ""
-
- [int]$subscriptionIndex = Read-Host "Enter the number of the subscription (1-$($subscriptionArray.Count)) to use"
-
- if ($subscriptionIndex -ge 1 -and $subscriptionIndex -le $subscriptionArray.Count) {
- $selectedSubscription = $subscriptionArray[$subscriptionIndex-1]
- $selectedSubscriptionName = $selectedSubscription.Name
- $selectedSubscriptionId = $selectedSubscription.Id
-
- # Set the selected subscription
- $result = az account set --subscription $selectedSubscriptionId
- if ($LASTEXITCODE -eq 0) {
- Write-Host "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
- break
- }
- else {
- Write-Host "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
- }
- }
- else {
- Write-Host "Invalid selection. Please try again."
- }
- } while ($true)
- }
- else {
- Write-Host "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription $currentSubscriptionId
- }
-}
-else {
- Write-Host "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription $currentSubscriptionId
-}
-
-$stIsPublicAccessDisabled = $false
-$srchIsPublicAccessDisabled = $false
-# Enable public access for resources
-if ($ResourceGroup) {
- $stPublicAccess = $(az storage account show --name $StorageAccount --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv)
- if ($stPublicAccess -eq "Disabled") {
- $stIsPublicAccessDisabled = $true
- Write-Host "Enabling public access for storage account: $StorageAccount"
- az storage account update --name $StorageAccount --public-network-access enabled --default-action Allow --output none
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Failed to enable public access for storage account."
- exit 1
- }
- }
- else {
- Write-Host "Public access is already enabled for storage account: $StorageAccount"
- }
-
- $srchPublicAccess = $(az search service show --name $AiSearch --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv)
- if ($srchPublicAccess -eq "Disabled") {
- $srchIsPublicAccessDisabled = $true
- Write-Host "Enabling public access for search service: $AiSearch"
- az search service update --name $AiSearch --resource-group $ResourceGroup --public-network-access enabled --output none
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Failed to enable public access for search service."
- exit 1
- }
- }
- else {
- Write-Host "Public access is already enabled for search service: $AiSearch"
- }
-}
-
-
-# Upload sample files to blob storage
-Write-Host "Uploading sample files to blob storage..."
-$result = az storage blob upload-batch --account-name $StorageAccount --destination $BlobContainer --source "data/datasets" --auth-mode login --pattern "*" --overwrite --output none
-
-if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Failed to upload files to blob storage."
- exit 1
-}
-Write-Host "Files uploaded successfully to blob storage."
-
-# Determine the correct Python command
-$pythonCmd = $null
-
-try {
- $pythonVersion = (python --version) 2>&1
- if ($pythonVersion -match "Python \d") {
- $pythonCmd = "python"
- }
-}
-catch {
- # Do nothing, try python3 next
-}
-
-if (-not $pythonCmd) {
- try {
- $pythonVersion = (python3 --version) 2>&1
- if ($pythonVersion -match "Python \d") {
- $pythonCmd = "python3"
- }
- }
- catch {
- Write-Host "Python is not installed on this system or it is not added in the PATH."
- exit 1
- }
-}
-
-if (-not $pythonCmd) {
- Write-Host "Python is not installed on this system or it is not added in the PATH."
- exit 1
-}
-
-# Create virtual environment
-$venvPath = "infra/scripts/scriptenv"
-if (Test-Path $venvPath) {
- Write-Host "Virtual environment already exists. Skipping creation."
-}
-else {
- Write-Host "Creating virtual environment"
- & $pythonCmd -m venv $venvPath
-}
-
-# Activate the virtual environment
-$activateScript = ""
-if (Test-Path (Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1")) {
- $activateScript = Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1"
-}
-elseif (Test-Path (Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1")) {
- $activateScript = Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1"
-}
-
-if ($activateScript) {
- Write-Host "Activating virtual environment"
- . $activateScript # Use dot sourcing to run in the current scope
-}
-else {
- Write-Host "Error activating virtual environment. Requirements may be installed globally."
-}
-
-# Install the requirements
-Write-Host "Installing requirements"
-pip install --quiet -r infra/scripts/requirements.txt
-Write-Host "Requirements installed"
-
-# Run the Python script to index data
-Write-Host "Running the python script to index data"
-$process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $StorageAccount, $BlobContainer, $AiSearch, $AiSearchIndex -Wait -NoNewWindow -PassThru
-
-if ($process.ExitCode -ne 0) {
- Write-Host "Error: Indexing python script execution failed."
- exit 1
-}
-
-#disable public access for resources
-if ($stIsPublicAccessDisabled) {
- Write-Host "Disabling public access for storage account: $StorageAccount"
- az storage account update --name $StorageAccount --public-network-access disabled --default-action Deny --output none
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Failed to disable public access for storage account."
- exit 1
- }
-}
-
-if ($srchIsPublicAccessDisabled) {
- Write-Host "Disabling public access for search service: $AiSearch"
- az search service update --name $AiSearch --resource-group $ResourceGroup --public-network-access disabled --output none
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Failed to disable public access for search service."
- exit 1
- }
-}
-
-Write-Host "Script executed successfully. Sample Data Processed Successfully."
diff --git a/infra/scripts/Selecting-Team-Config-And-Data.ps1 b/infra/scripts/Selecting-Team-Config-And-Data.ps1
new file mode 100644
index 000000000..9f9480cbb
--- /dev/null
+++ b/infra/scripts/Selecting-Team-Config-And-Data.ps1
@@ -0,0 +1,881 @@
+#Requires -Version 7.0
+
+param(
+ [string]$ResourceGroup
+)
+
+# Variables
+$directoryPath = ""
+$backendUrl = ""
+$storageAccount = ""
+$blobContainerForRetailCustomer = ""
+$blobContainerForRetailOrder = ""
+$blobContainerForRFPSummary = ""
+$blobContainerForRFPRisk = ""
+$blobContainerForRFPCompliance = ""
+$blobContainerForContractSummary = ""
+$blobContainerForContractRisk = ""
+$blobContainerForContractCompliance = ""
+$aiSearch = ""
+$aiSearchIndexForRetailCustomer = ""
+$aiSearchIndexForRetailOrder = ""
+$aiSearchIndexForRFPSummary = ""
+$aiSearchIndexForRFPRisk = ""
+$aiSearchIndexForRFPCompliance = ""
+$aiSearchIndexForContractSummary = ""
+$aiSearchIndexForContractRisk = ""
+$aiSearchIndexForContractCompliance = ""
+$azSubscriptionId = ""
+$stIsPublicAccessDisabled = $false
+$srchIsPublicAccessDisabled = $false
+
+# Cleanup function to restore network access
+function Restore-NetworkAccess {
+ if ($script:ResourceGroup -and $script:storageAccount -and $script:aiSearch) {
+ # Check resource group tag
+ $rgTypeTag = (az group show --name $script:ResourceGroup --query "tags.Type" -o tsv 2>$null)
+
+ if ($rgTypeTag -eq "WAF") {
+ if ($script:stIsPublicAccessDisabled -eq $true -or $script:srchIsPublicAccessDisabled -eq $true) {
+ Write-Host "=== Restoring network access settings ==="
+ }
+
+ if ($script:stIsPublicAccessDisabled -eq $true) {
+ $currentAccess = $(az storage account show --name $script:storageAccount --resource-group $script:ResourceGroup --query "publicNetworkAccess" -o tsv 2>$null)
+ if ($currentAccess -eq "Enabled") {
+ Write-Host "Disabling public access for Storage Account: $($script:storageAccount)"
+ az storage account update --name $script:storageAccount --public-network-access disabled --default-action Deny --output none 2>$null
+ Write-Host "โ Storage Account public access disabled"
+ } else {
+ Write-Host "โ Storage Account access unchanged (already at desired state)"
+ }
+ } else {
+ if ($script:ResourceGroup) {
+ $checkTag = (az group show --name $script:ResourceGroup --query "tags.Type" -o tsv 2>$null)
+ if ($checkTag -eq "WAF") {
+ if ($script:stIsPublicAccessDisabled -eq $false -and $script:srchIsPublicAccessDisabled -eq $false) {
+ Write-Host "=== Restoring network access settings ==="
+ }
+ Write-Host "โ Storage Account access unchanged (already at desired state)"
+ }
+ }
+ }
+
+ if ($script:srchIsPublicAccessDisabled -eq $true) {
+ $currentAccess = $(az search service show --name $script:aiSearch --resource-group $script:ResourceGroup --query "publicNetworkAccess" -o tsv 2>$null)
+ if ($currentAccess -eq "Enabled") {
+ Write-Host "Disabling public access for AI Search Service: $($script:aiSearch)"
+ az search service update --name $script:aiSearch --resource-group $script:ResourceGroup --public-network-access disabled --output none 2>$null
+ Write-Host "โ AI Search Service public access disabled"
+ } else {
+ Write-Host "โ AI Search Service access unchanged (already at desired state)"
+ }
+ } else {
+ if ($script:ResourceGroup) {
+ $checkTag = (az group show --name $script:ResourceGroup --query "tags.Type" -o tsv 2>$null)
+ if ($checkTag -eq "WAF") {
+ Write-Host "โ AI Search Service access unchanged (already at desired state)"
+ }
+ }
+ }
+
+ if ($script:stIsPublicAccessDisabled -eq $true -or $script:srchIsPublicAccessDisabled -eq $true) {
+ Write-Host "=========================================="
+ } else {
+ if ($script:ResourceGroup) {
+ $checkTag = (az group show --name $script:ResourceGroup --query "tags.Type" -o tsv 2>$null)
+ if ($checkTag -eq "WAF") {
+ Write-Host "=========================================="
+ }
+ }
+ }
+ }
+ }
+}
+
+function Test-AzdInstalled {
+ try {
+ $null = Get-Command azd -ErrorAction Stop
+ return $true
+ } catch {
+ return $false
+ }
+}
+
+function Get-ValuesFromAzdEnv {
+ if (-not (Test-AzdInstalled)) {
+ Write-Host "Error: Azure Developer CLI is not installed."
+ return $false
+ }
+
+ Write-Host "Getting values from azd environment..."
+
+ $script:directoryPath = "data/agent_teams"
+ $script:backendUrl = $(azd env get-value BACKEND_URL)
+ $script:storageAccount = $(azd env get-value AZURE_STORAGE_ACCOUNT_NAME)
+ $script:blobContainerForRetailCustomer = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER)
+ $script:blobContainerForRetailOrder = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RETAIL_ORDER)
+ $script:blobContainerForRFPSummary = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RFP_SUMMARY)
+ $script:blobContainerForRFPRisk = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RFP_RISK)
+ $script:blobContainerForRFPCompliance = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE)
+ $script:blobContainerForContractSummary = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY)
+ $script:blobContainerForContractRisk = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_CONTRACT_RISK)
+ $script:blobContainerForContractCompliance = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE)
+ $script:aiSearch = $(azd env get-value AZURE_AI_SEARCH_NAME)
+ $script:aiSearchIndexForRetailCustomer = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER)
+ $script:aiSearchIndexForRetailOrder = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER)
+ $script:aiSearchIndexForRFPSummary = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY)
+ $script:aiSearchIndexForRFPRisk = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RFP_RISK)
+ $script:aiSearchIndexForRFPCompliance = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE)
+ $script:aiSearchIndexForContractSummary = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY)
+ $script:aiSearchIndexForContractRisk = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK)
+ $script:aiSearchIndexForContractCompliance = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE)
+ $script:ResourceGroup = $(azd env get-value AZURE_RESOURCE_GROUP)
+
+ # Validate that we got all required values
+ if (-not $script:backendUrl -or -not $script:storageAccount -or -not $script:blobContainerForRetailCustomer -or -not $script:aiSearch -or -not $script:aiSearchIndexForRetailOrder -or -not $script:ResourceGroup) {
+ Write-Host "Error: Could not retrieve all required values from azd environment."
+ return $false
+ }
+
+ Write-Host "Successfully retrieved values from azd environment."
+ return $true
+}
+
+function Get-DeploymentValue {
+ param(
+ [object]$DeploymentOutputs,
+ [string]$PrimaryKey,
+ [string]$FallbackKey
+ )
+
+ $value = $null
+
+ # Try primary key first
+ if ($DeploymentOutputs.PSObject.Properties[$PrimaryKey]) {
+ $value = $DeploymentOutputs.$PrimaryKey.value
+ }
+
+ # If primary key failed, try fallback key
+ if (-not $value -and $DeploymentOutputs.PSObject.Properties[$FallbackKey]) {
+ $value = $DeploymentOutputs.$FallbackKey.value
+ }
+
+ return $value
+}
+
+function Get-ValuesFromAzDeployment {
+ Write-Host "Getting values from Azure deployment outputs..."
+
+ $script:directoryPath = "data/agent_teams"
+
+ Write-Host "Fetching deployment name..."
+ $deploymentName = az group show --name $ResourceGroup --query "tags.DeploymentName" -o tsv
+ if (-not $deploymentName) {
+ Write-Host "Error: Could not find deployment name in resource group tags."
+ return $false
+ }
+
+ Write-Host "Fetching deployment outputs for deployment: $deploymentName"
+ $deploymentOutputs = az deployment group show --resource-group $ResourceGroup --name $deploymentName --query "properties.outputs" -o json | ConvertFrom-Json
+ if (-not $deploymentOutputs) {
+ Write-Host "Error: Could not fetch deployment outputs."
+ return $false
+ }
+
+ # Extract specific outputs with fallback logic
+ $script:storageAccount = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_ACCOUNT_NAME" -FallbackKey "azureStorageAccountName"
+ $script:blobContainerForRetailCustomer = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER" -FallbackKey "azureStorageContainerNameRetailCustomer"
+ $script:blobContainerForRetailOrder = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_RETAIL_ORDER" -FallbackKey "azureStorageContainerNameRetailOrder"
+ $script:blobContainerForRFPSummary = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_RFP_SUMMARY" -FallbackKey "azureStorageContainerNameRfpSummary"
+ $script:blobContainerForRFPRisk = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_RFP_RISK" -FallbackKey "azureStorageContainerNameRfpRisk"
+ $script:blobContainerForRFPCompliance = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE" -FallbackKey "azureStorageContainerNameRfpCompliance"
+ $script:blobContainerForContractSummary = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY" -FallbackKey "azureStorageContainerNameContractSummary"
+ $script:blobContainerForContractRisk = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_CONTRACT_RISK" -FallbackKey "azureStorageContainerNameContractRisk"
+ $script:blobContainerForContractCompliance = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE" -FallbackKey "azureStorageContainerNameContractCompliance"
+ $script:aiSearchIndexForRetailCustomer = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER" -FallbackKey "azureAiSearchIndexNameRetailCustomer"
+ $script:aiSearchIndexForRetailOrder = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER" -FallbackKey "azureAiSearchIndexNameRetailOrder"
+ $script:aiSearchIndexForRFPSummary = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY" -FallbackKey "azureAiSearchIndexNameRfpSummary"
+ $script:aiSearchIndexForRFPRisk = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_RFP_RISK" -FallbackKey "azureAiSearchIndexNameRfpRisk"
+ $script:aiSearchIndexForRFPCompliance = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE" -FallbackKey "azureAiSearchIndexNameRfpCompliance"
+ $script:aiSearchIndexForContractSummary = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY" -FallbackKey "azureAiSearchIndexNameContractSummary"
+ $script:aiSearchIndexForContractRisk = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK" -FallbackKey "azureAiSearchIndexNameContractRisk"
+ $script:aiSearchIndexForContractCompliance = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE" -FallbackKey "azureAiSearchIndexNameContractCompliance"
+ $script:aiSearch = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_NAME" -FallbackKey "azureAiSearchName"
+ $script:backendUrl = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "backenD_URL" -FallbackKey "backendUrl"
+
+ # Validate that we extracted all required values
+ if (-not $script:storageAccount -or -not $script:aiSearch -or -not $script:backendUrl) {
+ Write-Host "Error: Could not extract all required values from deployment outputs."
+ return $false
+ }
+
+ Write-Host "Successfully retrieved values from deployment outputs."
+ return $true
+}
+
+function Get-ValuesUsingSolutionSuffix {
+ Write-Host "Getting values from resource naming convention using solution suffix..."
+
+ # Get the solution suffix from resource group tags
+ $solutionSuffix = az group show --name $ResourceGroup --query "tags.SolutionSuffix" -o tsv
+ if (-not $solutionSuffix) {
+ Write-Host "Error: Could not find SolutionSuffix tag in resource group."
+ return $false
+ }
+
+ Write-Host "Found solution suffix: $solutionSuffix"
+
+ # Reconstruct resource names using same naming convention as Bicep
+ $script:storageAccount = "st$solutionSuffix" -replace '-', '' # Remove dashes like Bicep does
+ $script:aiSearch = "srch-$solutionSuffix"
+ $containerAppName = "ca-$solutionSuffix"
+
+ # Query dynamic value (backend URL) from Container App
+ Write-Host "Querying backend URL from Container App..."
+ $backendFqdn = az containerapp show `
+ --name $containerAppName `
+ --resource-group $ResourceGroup `
+ --query "properties.configuration.ingress.fqdn" `
+ -o tsv 2>$null
+
+ if (-not $backendFqdn) {
+ Write-Host "Error: Could not get Container App FQDN. Container App may not be deployed yet."
+ return $false
+ }
+
+ $script:backendUrl = "https://$backendFqdn"
+
+ # Hardcoded container names (These don't follow the suffix pattern in Bicep, hence need to be changed here if changed in Bicep)
+ $script:blobContainerForRetailCustomer = "retail-dataset-customer"
+ $script:blobContainerForRetailOrder = "retail-dataset-order"
+ $script:blobContainerForRFPSummary = "rfp-summary-dataset"
+ $script:blobContainerForRFPRisk = "rfp-risk-dataset"
+ $script:blobContainerForRFPCompliance = "rfp-compliance-dataset"
+ $script:blobContainerForContractSummary = "contract-summary-dataset"
+ $script:blobContainerForContractRisk = "contract-risk-dataset"
+ $script:blobContainerForContractCompliance = "contract-compliance-dataset"
+
+ # Hardcoded index names (These don't follow the suffix pattern in Bicep, hence need to be changed here if changed in Bicep)
+ $script:aiSearchIndexForRetailCustomer = "macae-retail-customer-index"
+ $script:aiSearchIndexForRetailOrder = "macae-retail-order-index"
+ $script:aiSearchIndexForRFPSummary = "macae-rfp-summary-index"
+ $script:aiSearchIndexForRFPRisk = "macae-rfp-risk-index"
+ $script:aiSearchIndexForRFPCompliance = "macae-rfp-compliance-index"
+ $script:aiSearchIndexForContractSummary = "contract-summary-doc-index"
+ $script:aiSearchIndexForContractRisk = "contract-risk-doc-index"
+ $script:aiSearchIndexForContractCompliance = "contract-compliance-doc-index"
+
+ $script:directoryPath = "data/agent_teams"
+
+ # Validate that we got all critical values
+ if (-not $script:storageAccount -or -not $script:aiSearch -or -not $script:backendUrl) {
+ Write-Host "Error: Failed to reconstruct all required resource names."
+ return $false
+ }
+
+ Write-Host "Successfully reconstructed values from resource naming convention."
+ return $true
+}
+
+# Main script execution with cleanup handling
+try {
+# Authenticate with Azure
+try {
+ $null = az account show 2>$null
+ Write-Host "Already authenticated with Azure."
+} catch {
+ Write-Host "Not authenticated with Azure. Attempting to authenticate..."
+ Write-Host "Authenticating with Azure CLI..."
+ az login
+}
+
+# Get subscription ID from azd if available
+if (Test-AzdInstalled) {
+ try {
+ $azSubscriptionId = $(azd env get-value AZURE_SUBSCRIPTION_ID)
+ if (-not $azSubscriptionId) {
+ $azSubscriptionId = $env:AZURE_SUBSCRIPTION_ID
+ }
+ } catch {
+ $azSubscriptionId = ""
+ }
+}
+
+# Check if user has selected the correct subscription
+$currentSubscriptionId = az account show --query id -o tsv
+$currentSubscriptionName = az account show --query name -o tsv
+
+if ($currentSubscriptionId -ne $azSubscriptionId -and $azSubscriptionId) {
+ Write-Host "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
+ $confirmation = Read-Host "Do you want to continue with this subscription?(y/n)"
+ if ($confirmation -notin @("y", "Y")) {
+ Write-Host "Fetching available subscriptions..."
+ $availableSubscriptions = az account list --query "[?state=='Enabled'].[name,id]" --output tsv
+ $subscriptions = $availableSubscriptions -split "`n" | ForEach-Object { $_.Split("`t") }
+
+ do {
+ Write-Host ""
+ Write-Host "Available Subscriptions:"
+ Write-Host "========================"
+ for ($i = 0; $i -lt $subscriptions.Count; $i += 2) {
+ $index = ($i / 2) + 1
+ Write-Host "$index. $($subscriptions[$i]) ( $($subscriptions[$i + 1]) )"
+ }
+ Write-Host "========================"
+ Write-Host ""
+
+ $subscriptionIndex = Read-Host "Enter the number of the subscription (1-$(($subscriptions.Count / 2))) to use"
+
+ if ($subscriptionIndex -match '^\d+$' -and [int]$subscriptionIndex -ge 1 -and [int]$subscriptionIndex -le ($subscriptions.Count / 2)) {
+ $selectedIndex = ([int]$subscriptionIndex - 1) * 2
+ $selectedSubscriptionName = $subscriptions[$selectedIndex]
+ $selectedSubscriptionId = $subscriptions[$selectedIndex + 1]
+
+ try {
+ az account set --subscription $selectedSubscriptionId
+ Write-Host "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
+ $azSubscriptionId = $selectedSubscriptionId
+ break
+ } catch {
+ Write-Host "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
+ }
+ } else {
+ Write-Host "Invalid selection. Please try again."
+ }
+ } while ($true)
+ } else {
+ Write-Host "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
+ az account set --subscription $currentSubscriptionId
+ $azSubscriptionId = $currentSubscriptionId
+ }
+} else {
+ Write-Host "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
+ az account set --subscription $currentSubscriptionId
+ $azSubscriptionId = $currentSubscriptionId
+}
+
+# Get configuration values based on strategy
+if (-not $ResourceGroup) {
+ # No resource group provided - use azd env
+ if (-not (Get-ValuesFromAzdEnv)) {
+ Write-Host "Failed to get values from azd environment."
+ Write-Host "If you want to use deployment outputs instead, please provide the resource group name as an argument."
+ Write-Host "Usage: .\Team-Config-And-Data.ps1 [-ResourceGroup ]"
+ exit 1
+ }
+} else {
+ # Resource group provided - try deployment outputs first, then fallback to naming convention
+ Write-Host "Resource group provided: $ResourceGroup"
+
+ if (-not (Get-ValuesFromAzDeployment)) {
+ Write-Host ""
+ Write-Host "Warning: Could not retrieve values from deployment outputs (deployment may be deleted)."
+ Write-Host "Attempting fallback method: reconstructing values from resource naming convention..."
+ Write-Host ""
+
+ if (-not (Get-ValuesUsingSolutionSuffix)) {
+ Write-Host ""
+ Write-Host "Error: Both methods failed to retrieve configuration values."
+ Write-Host "Please ensure:"
+ Write-Host " 1. The deployment exists and has a DeploymentName tag, OR"
+ Write-Host " 2. The resource group has a SolutionSuffix tag"
+ exit 1
+ }
+ }
+}
+
+# Interactive Use Case Selection
+Write-Host ""
+Write-Host "==============================================="
+Write-Host "Available Use Cases:"
+Write-Host "==============================================="
+Write-Host "1. RFP Evaluation"
+Write-Host "2. Retail Customer Satisfaction"
+Write-Host "3. HR Employee Onboarding"
+Write-Host "4. Marketing Press Release"
+Write-Host "5. Contract Compliance Review"
+Write-Host "6. All"
+Write-Host "==============================================="
+Write-Host ""
+
+# Prompt user for use case selection
+do {
+ $useCaseSelection = Read-Host "Please enter the number of the use case you would like to install."
+
+ # Handle both numeric and text input for 'all'
+ if ($useCaseSelection -eq "all" -or $useCaseSelection -eq "6") {
+ $selectedUseCase = "All"
+ $useCaseValid = $true
+ Write-Host "Selected: All use cases will be installed."
+ }
+ elseif ($useCaseSelection -eq "1") {
+ $selectedUseCase = "RFP Evaluation"
+ $useCaseValid = $true
+ Write-Host "Selected: RFP Evaluation"
+ Write-Host "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ }
+ elseif ($useCaseSelection -eq "2") {
+ $selectedUseCase = "Retail Customer Satisfaction"
+ $useCaseValid = $true
+ Write-Host "Selected: Retail Customer Satisfaction"
+ Write-Host "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ }
+ elseif ($useCaseSelection -eq "3") {
+ $selectedUseCase = "HR Employee Onboarding"
+ $useCaseValid = $true
+ Write-Host "Selected: HR Employee Onboarding"
+ Write-Host "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ }
+ elseif ($useCaseSelection -eq "4") {
+ $selectedUseCase = "Marketing Press Release"
+ $useCaseValid = $true
+ Write-Host "Selected: Marketing Press Release"
+ Write-Host "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ }
+ elseif ($useCaseSelection -eq "5") {
+ $selectedUseCase = "Contract Compliance Review"
+ $useCaseValid = $true
+ Write-Host "Selected: Contract Compliance Review"
+ Write-Host "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ }
+ else {
+ $useCaseValid = $false
+ Write-Host "Invalid selection. Please enter a number from 1-6." -ForegroundColor Red
+ }
+} while (-not $useCaseValid)
+
+Write-Host ""
+Write-Host "==============================================="
+Write-Host "Values to be used:"
+Write-Host "==============================================="
+Write-Host "Selected Use Case: $selectedUseCase"
+Write-Host "Resource Group: $ResourceGroup"
+Write-Host "Backend URL: $backendUrl"
+Write-Host "Storage Account: $storageAccount"
+Write-Host "AI Search: $aiSearch"
+Write-Host "Directory Path: $directoryPath"
+Write-Host "Subscription ID: $azSubscriptionId"
+Write-Host "==============================================="
+Write-Host ""
+
+
+$userPrincipalId = $(az ad signed-in-user show --query id -o tsv)
+
+# Determine the correct Python command
+$pythonCmd = $null
+
+try {
+ $pythonVersion = (python --version) 2>&1
+ if ($pythonVersion -match "Python \d") {
+ $pythonCmd = "python"
+ }
+}
+catch {
+ # Do nothing, try python3 next
+}
+
+if (-not $pythonCmd) {
+ try {
+ $pythonVersion = (python3 --version) 2>&1
+ if ($pythonVersion -match "Python \d") {
+ $pythonCmd = "python3"
+ }
+ }
+ catch {
+ Write-Host "Python is not installed on this system or it is not added in the PATH."
+ exit 1
+ }
+}
+
+if (-not $pythonCmd) {
+ Write-Host "Python is not installed on this system or it is not added in the PATH."
+ exit 1
+}
+
+# Create virtual environment
+$venvPath = "infra/scripts/scriptenv"
+if (Test-Path $venvPath) {
+ Write-Host "Virtual environment already exists. Skipping creation."
+} else {
+ Write-Host "Creating virtual environment"
+ & $pythonCmd -m venv $venvPath
+}
+
+# Activate the virtual environment
+$activateScript = ""
+if (Test-Path (Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1")) {
+ $activateScript = Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1"
+} elseif (Test-Path (Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1")) {
+ $activateScript = Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1"
+}
+if ($activateScript) {
+ Write-Host "Activating virtual environment"
+ . $activateScript
+} else {
+ Write-Host "Error activating virtual environment. Requirements may be installed globally."
+}
+
+# Install the requirements
+Write-Host "Installing requirements"
+pip install --quiet -r infra/scripts/requirements.txt
+Write-Host "Requirements installed"
+
+$isTeamConfigFailed = $false
+$isSampleDataFailed = $false
+$failedTeamConfigs = 0
+
+# Use Case 3 -----=--
+if($useCaseSelection -eq "3" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6") {
+ Write-Host "Uploading Team Configuration for HR Employee Onboarding..."
+ $directoryPath = "data/agent_teams"
+ $teamId = "00000000-0000-0000-0000-000000000001"
+ try {
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/upload_team_config.py", $backendUrl, $directoryPath, $userPrincipalId, $teamId -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Team configuration for HR Employee Onboarding upload failed."
+ $isTeamConfigFailed = $true
+ }
+ } catch {
+ Write-Host "Error: Uploading team configuration failed."
+ $isTeamConfigFailed = $true
+ $failedTeamConfigs += 1
+ }
+
+}
+
+# Use Case 4 -----=--
+if($useCaseSelection -eq "4" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6") {
+ Write-Host "Uploading Team Configuration for Marketing Press Release..."
+ $directoryPath = "data/agent_teams"
+ $teamId = "00000000-0000-0000-0000-000000000002"
+ try {
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/upload_team_config.py", $backendUrl, $directoryPath, $userPrincipalId, $teamId -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Team configuration for Marketing Press Release upload failed."
+ $isTeamConfigFailed = $true
+ }
+ } catch {
+ Write-Host "Error: Uploading team configuration failed."
+ $isTeamConfigFailed = $true
+ $failedTeamConfigs += 1
+ }
+
+}
+
+$stIsPublicAccessDisabled = $false
+$srchIsPublicAccessDisabled = $false
+# Enable public access for resources
+if($useCaseSelection -eq "1"-or $useCaseSelection -eq "2" -or $useCaseSelection -eq "5" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6"){
+ if ($ResourceGroup) {
+ # Check if resource group has Type=WAF tag
+ $rgTypeTag = (az group show --name $ResourceGroup --query "tags.Type" -o tsv 2>$null)
+
+ if ($rgTypeTag -eq "WAF") {
+ Write-Host ""
+ Write-Host "=== Temporarily enabling public network access for services ==="
+ $stPublicAccess = $(az storage account show --name $storageAccount --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv)
+ if ($stPublicAccess -eq "Disabled") {
+ $stIsPublicAccessDisabled = $true
+ Write-Host "Enabling public access for Storage Account: $storageAccount"
+ az storage account update --name $storageAccount --public-network-access enabled --default-action Allow --output none
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to enable public access for storage account."
+ exit 1
+ }
+
+ # Wait 30 seconds for the change to propagate
+ Write-Host "Waiting 30 seconds for public access to be enabled..."
+ Start-Sleep -Seconds 30
+
+ # Verify public access is enabled in a loop
+ Write-Host "Verifying public access is enabled..."
+ $maxRetries = 10
+ $retryCount = 0
+ while ($retryCount -lt $maxRetries) {
+ $currentAccess = $(az storage account show --name $storageAccount --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv)
+ if ($currentAccess -eq "Enabled") {
+ Write-Host "โ Storage Account public access enabled successfully"
+ break
+ } else {
+ Write-Host "Public access not yet enabled (attempt $($retryCount + 1)/$maxRetries). Waiting 5 seconds..."
+ Start-Sleep -Seconds 5
+ $retryCount++
+ }
+ }
+
+ if ($retryCount -eq $maxRetries) {
+ Write-Host "Warning: Public access verification timed out for storage account."
+ }
+ } else {
+ Write-Host "โ Storage Account public access already enabled"
+ }
+ }
+
+ if ($rgTypeTag -eq "WAF") {
+ $srchPublicAccess = $(az search service show --name $aiSearch --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv)
+ if ($srchPublicAccess -eq "Disabled") {
+ $srchIsPublicAccessDisabled = $true
+ Write-Host "Enabling public access for AI Search Service: $aiSearch"
+ az search service update --name $aiSearch --resource-group $ResourceGroup --public-network-access enabled --output none
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to enable public access for search service."
+ exit 1
+ }
+ Write-Host "Public access enabled"
+
+ # Wait 30 seconds for the change to propagate
+ Write-Host "Waiting 30 seconds for public access to be enabled..."
+ Start-Sleep -Seconds 30
+
+ # Verify public access is enabled in a loop
+ Write-Host "Verifying public access is enabled..."
+ $maxRetries = 10
+ $retryCount = 0
+ while ($retryCount -lt $maxRetries) {
+ $currentAccess = $(az search service show --name $aiSearch --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv)
+ if ($currentAccess -eq "Enabled") {
+ Write-Host "โ AI Search Service public access enabled successfully"
+ break
+ } else {
+ Write-Host "Public access not yet enabled (attempt $($retryCount + 1)/$maxRetries). Waiting 5 seconds..."
+ Start-Sleep -Seconds 5
+ $retryCount++
+ }
+ }
+
+ if ($retryCount -eq $maxRetries) {
+ Write-Host "Warning: Public access verification timed out for search service."
+ }
+ } else {
+ Write-Host "โ AI Search Service public access already enabled"
+ }
+ Write-Host "==========================================================="
+ Write-Host ""
+ }
+ }
+}
+
+
+
+if($useCaseSelection -eq "1" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6") {
+ Write-Host "Uploading Team Configuration for RFP Evaluation..."
+ $directoryPath = "data/agent_teams"
+ $teamId = "00000000-0000-0000-0000-000000000004"
+ try {
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/upload_team_config.py", $backendUrl, $directoryPath, $userPrincipalId, $teamId -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Team configuration for RFP Evaluation upload failed."
+ $failedTeamConfigs += 1
+ $isTeamConfigFailed = $true
+ }
+ } catch {
+ Write-Host "Error: Uploading team configuration failed."
+ $isTeamConfigFailed = $true
+ }
+ Write-Host "Uploaded Team Configuration for RFP Evaluation..."
+
+ $directoryPath = "data/datasets/rfp/summary"
+ # Upload sample files to blob storage
+ Write-Host "Uploading sample files to blob storage for RFP Evaluation..."
+ $result = az storage blob upload-batch --account-name $storageAccount --destination $blobContainerForRFPSummary --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+
+ $directoryPath = "data/datasets/rfp/risk"
+ $result = az storage blob upload-batch --account-name $storageAccount --destination $blobContainerForRFPRisk --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+
+ $directoryPath = "data/datasets/rfp/compliance"
+ # Upload sample files to blob storage
+ $result = az storage blob upload-batch --account-name $storageAccount --destination $blobContainerForRFPCompliance --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+ Write-Host "Files uploaded successfully to blob storage."
+
+ # Run the Python script to index data
+ Write-Host "Running the python script to index data for RFP Evaluation"
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, $blobContainerForRFPSummary , $aiSearch, $aiSearchIndexForRFPSummary -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ }
+
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, $blobContainerForRFPRisk , $aiSearch, $aiSearchIndexForRFPRisk -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ }
+
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, $blobContainerForRFPCompliance , $aiSearch, $aiSearchIndexForRFPCompliance -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ }
+ Write-Host "Python script to index data for RFP Evaluation successfully executed."
+}
+
+
+if($useCaseSelection -eq "5" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6") {
+ Write-Host "Uploading Team Configuration for Contract Compliance Review..."
+ $directoryPath = "data/agent_teams"
+ $teamId = "00000000-0000-0000-0000-000000000005"
+ try {
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/upload_team_config.py", $backendUrl, $directoryPath, $userPrincipalId, $teamId -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Team configuration for Contract Compliance Review upload failed."
+ $failedTeamConfigs += 1
+ $isTeamConfigFailed = $true
+ }
+ } catch {
+ Write-Host "Error: Uploading team configuration failed."
+ $isTeamConfigFailed = $true
+ }
+ Write-Host "Uploaded Team Configuration for Contract Compliance Review..."
+
+ $directoryPath = "data/datasets/contract_compliance/summary"
+ # Upload sample files to blob storage
+ Write-Host "Uploading sample files to blob storage for Contract Compliance Review..."
+ $result = az storage blob upload-batch --account-name $storageAccount --destination $blobContainerForContractSummary --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+
+ $directoryPath = "data/datasets/contract_compliance/risk"
+ $result = az storage blob upload-batch --account-name $storageAccount --destination $blobContainerForContractRisk --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+
+ $directoryPath = "data/datasets/contract_compliance/compliance"
+
+ $result = az storage blob upload-batch --account-name $storageAccount --destination $blobContainerForContractCompliance --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+ Write-Host "Files uploaded successfully to blob storage."
+
+ # Run the Python script to index data
+ Write-Host "Running the python script to index data for Contract Compliance Review"
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, $blobContainerForContractSummary , $aiSearch, $aiSearchIndexForContractSummary -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ }
+
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, $blobContainerForContractRisk , $aiSearch, $aiSearchIndexForContractRisk -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ }
+
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, $blobContainerForContractCompliance , $aiSearch, $aiSearchIndexForContractCompliance -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ }
+ Write-Host "Python script to index data for Contract Compliance Review successfully executed."
+}
+
+if($useCaseSelection -eq "2" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6") {
+ Write-Host "Uploading Team Configuration for Retail Customer Satisfaction..."
+ $directoryPath = "data/agent_teams"
+ $teamId = "00000000-0000-0000-0000-000000000003"
+ try {
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/upload_team_config.py", $backendUrl, $directoryPath, $userPrincipalId, $teamId -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Team configuration for Retail Customer Satisfaction upload failed."
+ $failedTeamConfigs += 1
+ $isTeamConfigFailed = $true
+ }
+ } catch {
+ Write-Host "Error: Uploading team configuration failed."
+ $isTeamConfigFailed = $true
+ }
+ Write-Host "Uploaded Team Configuration for Retail Customer Satisfaction..."
+
+ $directoryPath = "data/datasets/retail/customer"
+ # Upload sample files to blob storage
+ Write-Host "Uploading sample files to blob storage for Retail Customer Satisfaction ..."
+ $result = az storage blob upload-batch --account-name $storageAccount --destination "retail-dataset-customer" --source $directoryPath --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+
+ $directoryPath = "data/datasets/retail/order"
+ $result = az storage blob upload-batch --account-name $storageAccount --destination "retail-dataset-order" --source "data/datasets/retail/order" --auth-mode login --pattern "*" --overwrite --output none
+
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "Error: Failed to upload files to blob storage."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+ Write-Host "Files uploaded successfully to blob storage."
+
+ # Run the Python script to index data
+ Write-Host "Running the python script to index data for Retail Customer Satisfaction"
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, "retail-dataset-customer", $aiSearch, "macae-retail-customer-index" -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+ $process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $storageAccount, "retail-dataset-order" , $aiSearch, "macae-retail-order-index" -Wait -NoNewWindow -PassThru
+
+ if ($process.ExitCode -ne 0) {
+ Write-Host "Error: Indexing python script execution failed."
+ $isSampleDataFailed = $true
+ exit 1
+ }
+ Write-Host "Python script to index data for Retail Customer Satisfaction successfully executed."
+}
+
+if ($isTeamConfigFailed -or $isSampleDataFailed) {
+ Write-Host "`nOne or more tasks failed. Please check the error messages above."
+ exit 1
+} else {
+ if($useCaseSelection -eq "1"-or $useCaseSelection -eq "2" -or $useCaseSelection -eq "5" -or $useCaseSelection -eq "all" -or $useCaseSelection -eq "6"){
+ Write-Host "`nTeam configuration upload and sample data processing completed successfully."
+ }else {
+ Write-Host "`nTeam configuration upload completed successfully."
+ }
+
+}
+
+} finally {
+ # Cleanup: Restore network access
+ Write-Host ""
+ Restore-NetworkAccess
+}
diff --git a/infra/scripts/Team-Config-And-Data.ps1 b/infra/scripts/Team-Config-And-Data.ps1
deleted file mode 100644
index 65948cccb..000000000
--- a/infra/scripts/Team-Config-And-Data.ps1
+++ /dev/null
@@ -1,262 +0,0 @@
-#Requires -Version 7.0
-
-param(
- [string]$ResourceGroup
-)
-
-# Variables
-$directoryPath = ""
-$backendUrl = ""
-$storageAccount = ""
-$blobContainer = ""
-$aiSearch = ""
-$aiSearchIndex = ""
-$azSubscriptionId = ""
-
-function Test-AzdInstalled {
- try {
- $null = Get-Command azd -ErrorAction Stop
- return $true
- } catch {
- return $false
- }
-}
-
-function Get-ValuesFromAzdEnv {
- if (-not (Test-AzdInstalled)) {
- Write-Host "Error: Azure Developer CLI is not installed."
- return $false
- }
-
- Write-Host "Getting values from azd environment..."
-
- $script:directoryPath = "data/agent_teams"
- $script:backendUrl = $(azd env get-value BACKEND_URL)
- $script:storageAccount = $(azd env get-value AZURE_STORAGE_ACCOUNT_NAME)
- $script:blobContainer = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME)
- $script:aiSearch = $(azd env get-value AZURE_AI_SEARCH_NAME)
- $script:aiSearchIndex = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME)
- $script:ResourceGroup = $(azd env get-value AZURE_RESOURCE_GROUP)
-
- # Validate that we got all required values
- if (-not $script:backendUrl -or -not $script:storageAccount -or -not $script:blobContainer -or -not $script:aiSearch -or -not $script:aiSearchIndex -or -not $script:ResourceGroup) {
- Write-Host "Error: Could not retrieve all required values from azd environment."
- return $false
- }
-
- Write-Host "Successfully retrieved values from azd environment."
- return $true
-}
-
-function Get-DeploymentValue {
- param(
- [object]$DeploymentOutputs,
- [string]$PrimaryKey,
- [string]$FallbackKey
- )
-
- $value = $null
-
- # Try primary key first
- if ($DeploymentOutputs.PSObject.Properties[$PrimaryKey]) {
- $value = $DeploymentOutputs.$PrimaryKey.value
- }
-
- # If primary key failed, try fallback key
- if (-not $value -and $DeploymentOutputs.PSObject.Properties[$FallbackKey]) {
- $value = $DeploymentOutputs.$FallbackKey.value
- }
-
- return $value
-}
-
-function Get-ValuesFromAzDeployment {
- Write-Host "Getting values from Azure deployment outputs..."
-
- $script:directoryPath = "data/agent_teams"
-
- Write-Host "Fetching deployment name..."
- $deploymentName = az group show --name $ResourceGroup --query "tags.DeploymentName" -o tsv
- if (-not $deploymentName) {
- Write-Host "Error: Could not find deployment name in resource group tags."
- return $false
- }
-
- Write-Host "Fetching deployment outputs for deployment: $deploymentName"
- $deploymentOutputs = az deployment group show --resource-group $ResourceGroup --name $deploymentName --query "properties.outputs" -o json | ConvertFrom-Json
- if (-not $deploymentOutputs) {
- Write-Host "Error: Could not fetch deployment outputs."
- return $false
- }
-
- # Extract specific outputs with fallback logic
- $script:storageAccount = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_ACCOUNT_NAME" -FallbackKey "azureStorageAccountName"
- $script:blobContainer = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_STORAGE_CONTAINER_NAME" -FallbackKey "azureStorageContainerName"
- $script:aiSearch = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_NAME" -FallbackKey "azureAiSearchName"
- $script:aiSearchIndex = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "azurE_AI_SEARCH_INDEX_NAME" -FallbackKey "azureAiSearchIndexName"
- $script:backendUrl = Get-DeploymentValue -DeploymentOutputs $deploymentOutputs -PrimaryKey "backenD_URL" -FallbackKey "backendUrl"
-
- # Validate that we extracted all required values
- if (-not $script:storageAccount -or -not $script:blobContainer -or -not $script:aiSearch -or -not $script:aiSearchIndex -or -not $script:backendUrl) {
- Write-Host "Error: Could not extract all required values from deployment outputs."
- return $false
- }
-
- Write-Host "Successfully retrieved values from deployment outputs."
- return $true
-}
-
-# Authenticate with Azure
-try {
- $null = az account show 2>$null
- Write-Host "Already authenticated with Azure."
-} catch {
- Write-Host "Not authenticated with Azure. Attempting to authenticate..."
- Write-Host "Authenticating with Azure CLI..."
- az login
-}
-
-# Get subscription ID from azd if available
-if (Test-AzdInstalled) {
- try {
- $azSubscriptionId = $(azd env get-value AZURE_SUBSCRIPTION_ID)
- if (-not $azSubscriptionId) {
- $azSubscriptionId = $env:AZURE_SUBSCRIPTION_ID
- }
- } catch {
- $azSubscriptionId = ""
- }
-}
-
-# Check if user has selected the correct subscription
-$currentSubscriptionId = az account show --query id -o tsv
-$currentSubscriptionName = az account show --query name -o tsv
-
-if ($currentSubscriptionId -ne $azSubscriptionId -and $azSubscriptionId) {
- Write-Host "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
- $confirmation = Read-Host "Do you want to continue with this subscription?(y/n)"
- if ($confirmation -notin @("y", "Y")) {
- Write-Host "Fetching available subscriptions..."
- $availableSubscriptions = az account list --query "[?state=='Enabled'].[name,id]" --output tsv
- $subscriptions = $availableSubscriptions -split "`n" | ForEach-Object { $_.Split("`t") }
-
- do {
- Write-Host ""
- Write-Host "Available Subscriptions:"
- Write-Host "========================"
- for ($i = 0; $i -lt $subscriptions.Count; $i += 2) {
- $index = ($i / 2) + 1
- Write-Host "$index. $($subscriptions[$i]) ( $($subscriptions[$i + 1]) )"
- }
- Write-Host "========================"
- Write-Host ""
-
- $subscriptionIndex = Read-Host "Enter the number of the subscription (1-$(($subscriptions.Count / 2))) to use"
-
- if ($subscriptionIndex -match '^\d+$' -and [int]$subscriptionIndex -ge 1 -and [int]$subscriptionIndex -le ($subscriptions.Count / 2)) {
- $selectedIndex = ([int]$subscriptionIndex - 1) * 2
- $selectedSubscriptionName = $subscriptions[$selectedIndex]
- $selectedSubscriptionId = $subscriptions[$selectedIndex + 1]
-
- try {
- az account set --subscription $selectedSubscriptionId
- Write-Host "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
- $azSubscriptionId = $selectedSubscriptionId
- break
- } catch {
- Write-Host "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
- }
- } else {
- Write-Host "Invalid selection. Please try again."
- }
- } while ($true)
- } else {
- Write-Host "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription $currentSubscriptionId
- $azSubscriptionId = $currentSubscriptionId
- }
-} else {
- Write-Host "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription $currentSubscriptionId
- $azSubscriptionId = $currentSubscriptionId
-}
-
-# Get configuration values based on strategy
-if (-not $ResourceGroup) {
- # No resource group provided - use azd env
- if (-not (Get-ValuesFromAzdEnv)) {
- Write-Host "Failed to get values from azd environment."
- Write-Host "If you want to use deployment outputs instead, please provide the resource group name as an argument."
- Write-Host "Usage: .\Team-Config-And-Data.ps1 [-ResourceGroup ]"
- exit 1
- }
-} else {
- # Resource group provided - use deployment outputs
- Write-Host "Resource group provided: $ResourceGroup"
-
- if (-not (Get-ValuesFromAzDeployment)) {
- Write-Host "Failed to get values from deployment outputs."
- exit 1
- }
-}
-
-Write-Host ""
-Write-Host "==============================================="
-Write-Host "Values to be used:"
-Write-Host "==============================================="
-Write-Host "Resource Group: $ResourceGroup"
-Write-Host "Backend URL: $backendUrl"
-Write-Host "Storage Account: $storageAccount"
-Write-Host "Blob Container: $blobContainer"
-Write-Host "AI Search: $aiSearch"
-Write-Host "AI Search Index: $aiSearchIndex"
-Write-Host "Directory Path: $directoryPath"
-Write-Host "Subscription ID: $azSubscriptionId"
-Write-Host "==============================================="
-Write-Host ""
-
-# Check if all required arguments are provided
-if (-not $backendUrl -or -not $directoryPath -or -not $storageAccount -or -not $blobContainer -or -not $aiSearch -or -not $aiSearchIndex -or -not $ResourceGroup) {
- Write-Host "Error: Missing required configuration values."
- Write-Host "Usage: .\Team-Config-And-Data.ps1 [-ResourceGroup ]"
- exit 1
-}
-
-$isTeamConfigFailed = $false
-$isSampleDataFailed = $false
-
-# Upload Team Configuration
-Write-Host "Uploading Team Configuration..."
-try {
- .\infra\scripts\Upload-Team-Config.ps1 -backendUrl $backendUrl -DirectoryPath $directoryPath -AzSubscriptionId $azSubscriptionId
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Uploading team configuration failed."
- $isTeamConfigFailed = $true
- }
-} catch {
- Write-Host "Error: Uploading team configuration failed."
- $isTeamConfigFailed = $true
-}
-
-Write-Host "`n----------------------------------------"
-Write-Host "----------------------------------------`n"
-
-# Process Sample Data
-Write-Host "Processing Sample Data..."
-try {
- .\infra\scripts\Process-Sample-Data.ps1 -StorageAccount $storageAccount -BlobContainer $blobContainer -AiSearch $aiSearch -AiSearchIndex $aiSearchIndex -ResourceGroup $ResourceGroup -AzSubscriptionId $azSubscriptionId
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Error: Processing sample data failed."
- $isSampleDataFailed = $true
- }
-} catch {
- Write-Host "Error: Processing sample data failed."
- $isSampleDataFailed = $true
-}
-
-if ($isTeamConfigFailed -or $isSampleDataFailed) {
- Write-Host "`nOne or more tasks failed. Please check the error messages above."
- exit 1
-} else {
- Write-Host "`nBoth team configuration upload and sample data processing completed successfully."
-}
diff --git a/infra/scripts/Upload-Team-Config.ps1 b/infra/scripts/Upload-Team-Config.ps1
deleted file mode 100644
index 4d406358a..000000000
--- a/infra/scripts/Upload-Team-Config.ps1
+++ /dev/null
@@ -1,157 +0,0 @@
-#Requires -Version 7.0
-
-param(
- [string]$backendUrl,
- [string]$DirectoryPath,
- [string]$AzSubscriptionId
-)
-
-# Get parameters from azd env, if not provided
-if (-not $backendUrl) {
- $backendUrl = $(azd env get-value BACKEND_URL)
-}
-if (-not $DirectoryPath) {
- $DirectoryPath = "data/agent_teams"
-}
-
-if (-not $AzSubscriptionId) {
- $AzSubscriptionId = $(azd env get-value AZURE_SUBSCRIPTION_ID)
-}
-
-# Check if all required arguments are provided
-if (-not $backendUrl -or -not $DirectoryPath) {
- Write-Host "Usage: .\infra\scripts\Upload-Team-Config.ps1 -backendUrl -DirectoryPath "
- exit 1
-}
-
-# Authenticate with Azure
-try {
- $currentAzContext = az account show | ConvertFrom-Json -ErrorAction Stop
- Write-Host "Already authenticated with Azure."
-} catch {
- Write-Host "Not authenticated with Azure. Attempting to authenticate..."
- Write-Host "Authenticating with Azure CLI..."
- az login
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Authentication failed."
- exit 1
- }
- $currentAzContext = az account show | ConvertFrom-Json
-}
-
-# Check if user has selected the correct subscription
-$currentSubscriptionId = $currentAzContext.id
-$currentSubscriptionName = $currentAzContext.name
-if ($currentSubscriptionId -ne $AzSubscriptionId) {
- Write-Host "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
- $confirmation = Read-Host "Do you want to continue with this subscription? (y/n)"
- if ($confirmation.ToLower() -ne "y") {
- Write-Host "Fetching available subscriptions..."
- $availableSubscriptions = (az account list --query "[?state=='Enabled']" | ConvertFrom-Json -AsHashtable)
- $subscriptionArray = $availableSubscriptions | ForEach-Object {
- [PSCustomObject]@{ Name = $_.name; Id = $_.id }
- }
- do {
- Write-Host ""
- Write-Host "Available Subscriptions:"
- Write-Host "========================"
- for ($i = 0; $i -lt $subscriptionArray.Count; $i++) {
- Write-Host "$($i+1). $($subscriptionArray[$i].Name) ( $($subscriptionArray[$i].Id) )"
- }
- Write-Host "========================"
- Write-Host ""
- [int]$subscriptionIndex = Read-Host "Enter the number of the subscription (1-$($subscriptionArray.Count)) to use"
- if ($subscriptionIndex -ge 1 -and $subscriptionIndex -le $subscriptionArray.Count) {
- $selectedSubscription = $subscriptionArray[$subscriptionIndex-1]
- $selectedSubscriptionName = $selectedSubscription.Name
- $selectedSubscriptionId = $selectedSubscription.Id
- $result = az account set --subscription $selectedSubscriptionId
- if ($LASTEXITCODE -eq 0) {
- Write-Host "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
- break
- } else {
- Write-Host "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
- }
- } else {
- Write-Host "Invalid selection. Please try again."
- }
- } while ($true)
- } else {
- Write-Host "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription $currentSubscriptionId
- }
-} else {
- Write-Host "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription $currentSubscriptionId
-}
-
-$userPrincipalId = $(az ad signed-in-user show --query id -o tsv)
-
-# Determine the correct Python command
-$pythonCmd = $null
-
-try {
- $pythonVersion = (python --version) 2>&1
- if ($pythonVersion -match "Python \d") {
- $pythonCmd = "python"
- }
-}
-catch {
- # Do nothing, try python3 next
-}
-
-if (-not $pythonCmd) {
- try {
- $pythonVersion = (python3 --version) 2>&1
- if ($pythonVersion -match "Python \d") {
- $pythonCmd = "python3"
- }
- }
- catch {
- Write-Host "Python is not installed on this system or it is not added in the PATH."
- exit 1
- }
-}
-
-if (-not $pythonCmd) {
- Write-Host "Python is not installed on this system or it is not added in the PATH."
- exit 1
-}
-
-# Create virtual environment
-$venvPath = "infra/scripts/scriptenv"
-if (Test-Path $venvPath) {
- Write-Host "Virtual environment already exists. Skipping creation."
-} else {
- Write-Host "Creating virtual environment"
- & $pythonCmd -m venv $venvPath
-}
-
-# Activate the virtual environment
-$activateScript = ""
-if (Test-Path (Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1")) {
- $activateScript = Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1"
-} elseif (Test-Path (Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1")) {
- $activateScript = Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1"
-}
-if ($activateScript) {
- Write-Host "Activating virtual environment"
- . $activateScript
-} else {
- Write-Host "Error activating virtual environment. Requirements may be installed globally."
-}
-
-# Install the requirements
-Write-Host "Installing requirements"
-pip install --quiet -r infra/scripts/requirements.txt
-Write-Host "Requirements installed"
-
-# Run the Python script to upload team configuration
-Write-Host "Running the python script to upload team configuration"
-$process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/upload_team_config.py", $backendUrl, $DirectoryPath, $userPrincipalId -Wait -NoNewWindow -PassThru
-if ($process.ExitCode -ne 0) {
- Write-Host "Error: Team configuration upload failed."
- exit 1
-}
-
-Write-Host "Script executed successfully. Team configuration uploaded."
diff --git a/infra/scripts/checkquota.sh b/infra/scripts/checkquota.sh
index 6fcb64614..b79815716 100644
--- a/infra/scripts/checkquota.sh
+++ b/infra/scripts/checkquota.sh
@@ -7,16 +7,6 @@ SUBSCRIPTION_ID="${AZURE_SUBSCRIPTION_ID}"
GPT_MIN_CAPACITY="${GPT_MIN_CAPACITY}"
O4_MINI_MIN_CAPACITY="${O4_MINI_MIN_CAPACITY}"
GPT41_MINI_MIN_CAPACITY="${GPT41_MINI_MIN_CAPACITY}"
-AZURE_CLIENT_ID="${AZURE_CLIENT_ID}"
-AZURE_TENANT_ID="${AZURE_TENANT_ID}"
-AZURE_CLIENT_SECRET="${AZURE_CLIENT_SECRET}"
-
-# Authenticate using Managed Identity
-echo "Authentication using Managed Identity..."
-if ! az login --service-principal -u "$AZURE_CLIENT_ID" -p "$AZURE_CLIENT_SECRET" --tenant "$AZURE_TENANT_ID"; then
- echo "โ Error: Failed to login using Managed Identity."
- exit 1
-fi
echo "๐ Validating required environment variables..."
if [[ -z "$SUBSCRIPTION_ID" || -z "$REGIONS" ]]; then
diff --git a/infra/scripts/index_datasets.py b/infra/scripts/index_datasets.py
index 86a15d7b6..e0c799dff 100644
--- a/infra/scripts/index_datasets.py
+++ b/infra/scripts/index_datasets.py
@@ -5,6 +5,83 @@
from azure.storage.blob import BlobServiceClient
import sys
+
+# PDF text extraction function
+def extract_pdf_text(pdf_bytes):
+ """Extract text content from PDF bytes using PyPDF2"""
+ try:
+ import PyPDF2
+ import io
+
+ pdf_file = io.BytesIO(pdf_bytes)
+ pdf_reader = PyPDF2.PdfReader(pdf_file)
+
+ # Check if PDF is encrypted/protected
+ if pdf_reader.is_encrypted:
+ return "PDF_PROTECTED: This PDF document is password-protected or encrypted and cannot be processed."
+
+ text_content = []
+ for page in pdf_reader.pages:
+ try:
+ page_text = page.extract_text()
+ if page_text and page_text.strip():
+ text_content.append(page_text)
+ except Exception:
+ continue
+
+ full_text = "\n".join(text_content).strip()
+
+ # Check for protection messages
+ protection_indicators = [
+ "protected by Microsoft Office",
+ "You'll need a different reader",
+ "Download a compatible PDF reader",
+ "This PDF Document has been protected"
+ ]
+
+ if any(indicator.lower() in full_text.lower() for indicator in protection_indicators):
+ return "PDF_PROTECTED: This PDF document appears to be protected or encrypted."
+
+ return full_text if full_text else "PDF_NO_TEXT: No readable text content found in PDF."
+
+ except ImportError:
+ return "PDF_ERROR: PyPDF2 library not available. Install with: pip install PyPDF2"
+ except Exception as e:
+ return f"PDF_ERROR: Error reading PDF content: {str(e)}"
+
+
+# DOCX text extraction function
+def extract_docx_text(docx_bytes):
+ """Extract text content from DOCX bytes using python-docx"""
+ try:
+ from docx import Document
+ import io
+
+ docx_file = io.BytesIO(docx_bytes)
+ doc = Document(docx_file)
+
+ text_content = []
+
+ # Extract text from paragraphs
+ for paragraph in doc.paragraphs:
+ if paragraph.text.strip():
+ text_content.append(paragraph.text)
+
+ # Extract text from tables
+ for table in doc.tables:
+ for row in table.rows:
+ for cell in row.cells:
+ if cell.text.strip():
+ text_content.append(cell.text)
+
+ full_text = "\n".join(text_content).strip()
+ return full_text if full_text else "DOCX_NO_TEXT: No readable text content found in DOCX."
+
+ except ImportError:
+ return "DOCX_ERROR: python-docx library not available. Install with: pip install python-docx"
+ except Exception as e:
+ return f"DOCX_ERROR: Error reading DOCX content: {str(e)}"
+
if len(sys.argv) < 4:
print("Usage: python index_datasets.py []")
sys.exit(1)
@@ -41,7 +118,7 @@
print("Creating or updating Azure Search index...")
search_index_client = SearchIndexClient(endpoint=ai_search_endpoint, credential=credential)
- index_result = search_index_client.create_or_update_index(index=index)
+ search_index_client.create_or_update_index(index=index)
print(f"Index '{ai_search_index_name}' created or updated successfully.")
except Exception as e:
print(f"Error creating/updating index: {e}")
@@ -51,11 +128,22 @@
#if blob.name.endswith(".csv"):
title = blob.name.replace(".csv", "")
title = title.replace(".json", "")
+ title = title.replace(".pdf", "")
+ title = title.replace(".docx", "")
+ title = title.replace(".pptx", "")
data = container_client.download_blob(blob.name).readall()
try:
print(f"Reading data from blob: {blob.name}...")
- text = data.decode('utf-8')
+ #text = data.decode('utf-8')
+ # Check if this is a PDF file and process accordingly
+ if blob.name.lower().endswith('.pdf'):
+ text = extract_pdf_text(data)
+ elif blob.name.lower().endswith('.docx'):
+ text = extract_docx_text(data)
+ else:
+ # Original processing for non-PDF files
+ text = data.decode('utf-8')
data_list.append({
"content": text,
"id": str(idx),
@@ -75,7 +163,9 @@
print("Uploading documents to the index...")
search_client = SearchClient(endpoint=ai_search_endpoint, index_name=ai_search_index_name, credential=credential)
result = search_client.upload_documents(documents=data_list)
- print(f"Uploaded {len(data_list)} documents.")
+ successes = sum(1 for r in result if getattr(r, "succeeded", False))
+ failures = len(data_list) - successes
+ print(f"Uploaded documents. Requested: {len(data_list)}, Succeeded: {successes}, Failed: {failures}")
except Exception as e:
print(f"Error uploading documents: {e}")
sys.exit(1)
diff --git a/infra/scripts/process_sample_data.sh b/infra/scripts/process_sample_data.sh
deleted file mode 100644
index 2253be08c..000000000
--- a/infra/scripts/process_sample_data.sh
+++ /dev/null
@@ -1,199 +0,0 @@
-#!/bin/bash
-
-# Variables
-storageAccount="$1"
-blobContainer="$2"
-aiSearch="$3"
-aiSearchIndex="$4"
-resourceGroup="$5"
-azSubscriptionId="$6"
-
-# get parameters from azd env, if not provided
-if [ -z "$storageAccount" ]; then
- storageAccount=$(azd env get-value AZURE_STORAGE_ACCOUNT_NAME)
-fi
-
-if [ -z "$blobContainer" ]; then
- blobContainer=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME)
-fi
-
-if [ -z "$aiSearch" ]; then
- aiSearch=$(azd env get-value AZURE_AI_SEARCH_NAME)
-fi
-
-if [ -z "$aiSearchIndex" ]; then
- aiSearchIndex=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME)
-fi
-
-if [ -z "$resourceGroup" ]; then
- resourceGroup=$(azd env get-value AZURE_RESOURCE_GROUP)
-fi
-
-if [ -z "$azSubscriptionId" ]; then
- azSubscriptionId=$(azd env get-value AZURE_SUBSCRIPTION_ID)
-fi
-
-# Check if all required arguments are provided
-if [ -z "$storageAccount" ] || [ -z "$blobContainer" ] || [ -z "$aiSearch" ]; then
- echo "Usage: $0 [AISearchIndexName] [ResourceGroupName]"
- exit 1
-fi
-
-# Authenticate with Azure
-if az account show &> /dev/null; then
- echo "Already authenticated with Azure."
-else
- echo "Not authenticated with Azure. Attempting to authenticate..."
- echo "Authenticating with Azure CLI..."
- az login
-fi
-
-#check if user has selected the correct subscription
-currentSubscriptionId=$(az account show --query id -o tsv)
-currentSubscriptionName=$(az account show --query name -o tsv)
-if [ "$currentSubscriptionId" != "$azSubscriptionId" ]; then
- echo "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
- read -rp "Do you want to continue with this subscription?(y/n): " confirmation
- if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then
- echo "Fetching available subscriptions..."
- availableSubscriptions=$(az account list --query "[?state=='Enabled'].[name,id]" --output tsv)
- while true; do
- echo ""
- echo "Available Subscriptions:"
- echo "========================"
- echo "$availableSubscriptions" | awk '{printf "%d. %s ( %s )\n", NR, $1, $2}'
- echo "========================"
- echo ""
- read -rp "Enter the number of the subscription (1-$(echo "$availableSubscriptions" | wc -l)) to use: " subscriptionIndex
- if [[ "$subscriptionIndex" =~ ^[0-9]+$ ]] && [ "$subscriptionIndex" -ge 1 ] && [ "$subscriptionIndex" -le $(echo "$availableSubscriptions" | wc -l) ]; then
- selectedSubscription=$(echo "$availableSubscriptions" | sed -n "${subscriptionIndex}p")
- selectedSubscriptionName=$(echo "$selectedSubscription" | cut -f1)
- selectedSubscriptionId=$(echo "$selectedSubscription" | cut -f2)
-
- # Set the selected subscription
- if az account set --subscription "$selectedSubscriptionId"; then
- echo "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
- break
- else
- echo "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
- fi
- else
- echo "Invalid selection. Please try again."
- fi
- done
- else
- echo "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription "$currentSubscriptionId"
- fi
-else
- echo "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription "$currentSubscriptionId"
-fi
-
-stIsPublicAccessDisabled=false
-srchIsPublicAccessDisabled=false
-#Enable Public Access for resources
-if [ -n "$resourceGroup" ]; then
- stPublicAccess=$(az storage account show --name "$storageAccount" --resource-group "$resourceGroup" --query "publicNetworkAccess" -o tsv)
- srchPublicAccess=$(az search service show --name "$aiSearch" --resource-group "$resourceGroup" --query "publicNetworkAccess" -o tsv)
- if [ "$stPublicAccess" == "Disabled" ]; then
- stIsPublicAccessDisabled=true
- echo "Enabling public access for storage account: $storageAccount"
- az storage account update --name "$storageAccount" --public-network-access enabled --default-action Allow --output none
- if [ $? -ne 0 ]; then
- echo "Error: Failed to enable public access for storage account."
- exit 1
- fi
- echo "Public access enabled for storage account: $storageAccount"
- else
- echo "Public access is already enabled for storage account: $storageAccount"
- fi
-
- if [ "$srchPublicAccess" == "Disabled" ]; then
- srchIsPublicAccessDisabled=true
- echo "Enabling public access for search service: $aiSearch"
- az search service update --name "$aiSearch" --resource-group "$resourceGroup" --public-network-access enabled --output none
- if [ $? -ne 0 ]; then
- echo "Error: Failed to enable public access for search service."
- exit 1
- fi
- echo "Public access enabled for search service: $aiSearch"
- else
- echo "Public access is already enabled for search service: $aiSearch"
- fi
-
-fi
-
-
-#Upload sample files to blob storage
-echo "Uploading sample files to blob storage..."
-az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainer" --source "data/datasets" --auth-mode login --pattern '*' --overwrite --output none
-if [ $? -ne 0 ]; then
- echo "Error: Failed to upload files to blob storage."
- exit 1
-fi
-echo "Files uploaded successfully to blob storage."
-
-# Determine the correct Python command
-if command -v python && python --version &> /dev/null; then
- PYTHON_CMD="python"
-elif command -v python3 && python3 --version &> /dev/null; then
- PYTHON_CMD="python3"
-else
- echo "Python is not installed on this system. Or it is not added in the PATH."
- exit 1
-fi
-
-# create virtual environment
-if [ -d "infra/scripts/scriptenv" ]; then
- echo "Virtual environment already exists. Skipping creation."
-else
- echo "Creating virtual environment"
- $PYTHON_CMD -m venv infra/scripts/scriptenv
-fi
-
-# Activate the virtual environment
-if [ -f "infra/scripts/scriptenv/bin/activate" ]; then
- echo "Activating virtual environment (Linux/macOS)"
- source "infra/scripts/scriptenv/bin/activate"
-elif [ -f "infra/scripts/scriptenv/Scripts/activate" ]; then
- echo "Activating virtual environment (Windows)"
- source "infra/scripts/scriptenv/Scripts/activate"
-else
- echo "Error activating virtual environment. Requirements may be installed globally."
-fi
-
-# Install the requirements
-echo "Installing requirements"
-pip install --quiet -r infra/scripts/requirements.txt
-echo "Requirements installed"
-
-echo "Running the python script to index data"
-$PYTHON_CMD infra/scripts/index_datasets.py "$storageAccount" "$blobContainer" "$aiSearch" "$aiSearchIndex"
-if [ $? -ne 0 ]; then
- echo "Error: Indexing python script execution failed."
- exit 1
-fi
-
-#disable public access for resources
-if [ "$stIsPublicAccessDisabled" = true ]; then
- echo "Disabling public access for storage account: $storageAccount"
- az storage account update --name "$storageAccount" --public-network-access disabled --default-action Deny --output none
- if [ $? -ne 0 ]; then
- echo "Error: Failed to disable public access for storage account."
- exit 1
- fi
- echo "Public access disabled for storage account: $storageAccount"
-fi
-
-if [ "$srchIsPublicAccessDisabled" = true ]; then
- echo "Disabling public access for search service: $aiSearch"
- az search service update --name "$aiSearch" --resource-group "$resourceGroup" --public-network-access disabled --output none
- if [ $? -ne 0 ]; then
- echo "Error: Failed to disable public access for search service."
- exit 1
- fi
- echo "Public access disabled for search service: $aiSearch"
-fi
-
-echo "Script executed successfully. Sample Data Processed Successfully."
\ No newline at end of file
diff --git a/infra/scripts/quota_check_params.sh b/infra/scripts/quota_check_params.sh
index f1a15f939..e46f88011 100644
--- a/infra/scripts/quota_check_params.sh
+++ b/infra/scripts/quota_check_params.sh
@@ -47,7 +47,7 @@ log_verbose() {
}
# Default Models and Capacities (Comma-separated in "model:capacity" format)
-DEFAULT_MODEL_CAPACITY="gpt-4o:150"
+DEFAULT_MODEL_CAPACITY="gpt4.1:150,o4-mini:50,gpt4.1-mini:50"
# Convert the comma-separated string into an array
IFS=',' read -r -a MODEL_CAPACITY_PAIRS <<< "$DEFAULT_MODEL_CAPACITY"
diff --git a/infra/scripts/requirements.txt b/infra/scripts/requirements.txt
index 67ba55e5a..aa9db8f5d 100644
--- a/infra/scripts/requirements.txt
+++ b/infra/scripts/requirements.txt
@@ -1,4 +1,7 @@
azure-search-documents==11.5.3
azure-identity==1.24.0
azure-storage-blob==12.26.0
-requests==2.32.5
\ No newline at end of file
+requests==2.33.0
+azure-core
+PyPDF2
+python-docx
\ No newline at end of file
diff --git a/infra/scripts/selecting_team_config_and_data.sh b/infra/scripts/selecting_team_config_and_data.sh
new file mode 100644
index 000000000..ee6b273a1
--- /dev/null
+++ b/infra/scripts/selecting_team_config_and_data.sh
@@ -0,0 +1,831 @@
+#!/bin/bash
+
+# Parse command line arguments
+ResourceGroup=""
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --resource-group)
+ ResourceGroup="$2"
+ shift 2
+ ;;
+ *)
+ echo "Unknown option: $1"
+ exit 1
+ ;;
+ esac
+done
+
+# Variables
+directoryPath=""
+backendUrl=""
+storageAccount=""
+blobContainerForRetailCustomer=""
+blobContainerForRetailOrder=""
+blobContainerForRFPSummary=""
+blobContainerForRFPRisk=""
+blobContainerForRFPCompliance=""
+blobContainerForContractSummary=""
+blobContainerForContractRisk=""
+blobContainerForContractCompliance=""
+aiSearch=""
+aiSearchIndexForRetailCustomer=""
+aiSearchIndexForRetailOrder=""
+aiSearchIndexForRFPSummary=""
+aiSearchIndexForRFPRisk=""
+aiSearchIndexForRFPCompliance=""
+aiSearchIndexForContractSummary=""
+aiSearchIndexForContractRisk=""
+aiSearchIndexForContractCompliance=""
+azSubscriptionId=""
+stIsPublicAccessDisabled=false
+srchIsPublicAccessDisabled=false
+
+# Cleanup function to restore network access
+restore_network_access() {
+ if [[ -n "$ResourceGroup" && -n "$storageAccount" && -n "$aiSearch" ]]; then
+ # Check resource group tag
+ local rgTypeTag=$(az group show --name "$ResourceGroup" --query "tags.Type" -o tsv 2>/dev/null)
+
+ if [[ "$rgTypeTag" == "WAF" ]]; then
+ if [[ "$stIsPublicAccessDisabled" == true || "$srchIsPublicAccessDisabled" == true ]]; then
+ echo "=== Restoring network access settings ==="
+ fi
+
+ if [[ "$stIsPublicAccessDisabled" == true ]]; then
+ currentAccess=$(az storage account show --name "$storageAccount" --resource-group "$ResourceGroup" --query "publicNetworkAccess" -o tsv 2>/dev/null)
+ if [[ "$currentAccess" == "Enabled" ]]; then
+ echo "Disabling public access for Storage Account: $storageAccount"
+ az storage account update --name "$storageAccount" --public-network-access disabled --default-action Deny --output none 2>/dev/null
+ echo "โ Storage Account public access disabled"
+ else
+ echo "โ Storage Account access unchanged (already at desired state)"
+ fi
+ else
+ if [[ -n "$ResourceGroup" ]]; then
+ local checkTag=$(az group show --name "$ResourceGroup" --query "tags.Type" -o tsv 2>/dev/null)
+ if [[ "$checkTag" == "WAF" ]]; then
+ if [[ "$stIsPublicAccessDisabled" == false && "$srchIsPublicAccessDisabled" == false ]]; then
+ if [[ "$stIsPublicAccessDisabled" == false ]]; then
+ echo "=== Restoring network access settings ==="
+ fi
+ fi
+ echo "โ Storage Account access unchanged (already at desired state)"
+ fi
+ fi
+ fi
+
+ if [[ "$srchIsPublicAccessDisabled" == true ]]; then
+ currentAccess=$(az search service show --name "$aiSearch" --resource-group "$ResourceGroup" --query "publicNetworkAccess" -o tsv 2>/dev/null)
+ if [[ "$currentAccess" == "Enabled" ]]; then
+ echo "Disabling public access for AI Search Service: $aiSearch"
+ az search service update --name "$aiSearch" --resource-group "$ResourceGroup" --public-network-access disabled --output none 2>/dev/null
+ echo "โ AI Search Service public access disabled"
+ else
+ echo "โ AI Search Service access unchanged (already at desired state)"
+ fi
+ else
+ if [[ -n "$ResourceGroup" ]]; then
+ local checkTag=$(az group show --name "$ResourceGroup" --query "tags.Type" -o tsv 2>/dev/null)
+ if [[ "$checkTag" == "WAF" ]]; then
+ echo "โ AI Search Service access unchanged (already at desired state)"
+ fi
+ fi
+ fi
+
+ if [[ "$stIsPublicAccessDisabled" == true || "$srchIsPublicAccessDisabled" == true ]]; then
+ echo "=========================================="
+ else
+ if [[ -n "$ResourceGroup" ]]; then
+ local checkTag=$(az group show --name "$ResourceGroup" --query "tags.Type" -o tsv 2>/dev/null)
+ if [[ "$checkTag" == "WAF" ]]; then
+ echo "=========================================="
+ fi
+ fi
+ fi
+ fi
+ fi
+}
+
+# Cleanup on exit handler
+cleanup_on_exit() {
+ exit_code=$?
+ echo ""
+ if [ $exit_code -ne 0 ]; then
+ echo ""
+ echo "Script failed with exit code: $exit_code"
+ fi
+ restore_network_access
+ exit $exit_code
+}
+
+# Set up trap to ensure cleanup happens on exit
+trap cleanup_on_exit EXIT TERM
+
+function test_azd_installed() {
+ if command -v azd &> /dev/null; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function get_values_from_azd_env() {
+ if ! test_azd_installed; then
+ echo "Error: Azure Developer CLI is not installed."
+ return 1
+ fi
+
+ echo "Getting values from azd environment..."
+
+ directoryPath="data/agent_teams"
+ backendUrl=$(azd env get-value BACKEND_URL)
+ storageAccount=$(azd env get-value AZURE_STORAGE_ACCOUNT_NAME)
+ blobContainerForRetailCustomer=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER)
+ blobContainerForRetailOrder=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RETAIL_ORDER)
+ blobContainerForRFPSummary=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RFP_SUMMARY)
+ blobContainerForRFPRisk=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RFP_RISK)
+ blobContainerForRFPCompliance=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE)
+ blobContainerForContractSummary=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY)
+ blobContainerForContractRisk=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_CONTRACT_RISK)
+ blobContainerForContractCompliance=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE)
+ aiSearch=$(azd env get-value AZURE_AI_SEARCH_NAME)
+ aiSearchIndexForRetailCustomer=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER)
+ aiSearchIndexForRetailOrder=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER)
+ aiSearchIndexForRFPSummary=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY)
+ aiSearchIndexForRFPRisk=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RFP_RISK)
+ aiSearchIndexForRFPCompliance=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE)
+ aiSearchIndexForContractSummary=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY)
+ aiSearchIndexForContractRisk=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK)
+ aiSearchIndexForContractCompliance=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE)
+ ResourceGroup=$(azd env get-value AZURE_RESOURCE_GROUP)
+
+ # Validate that we got all required values
+ if [[ -z "$backendUrl" || -z "$storageAccount" || -z "$blobContainerForRetailCustomer" || -z "$aiSearch" || -z "$aiSearchIndexForRetailOrder" || -z "$ResourceGroup" ]]; then
+ echo "Error: Could not retrieve all required values from azd environment."
+ return 1
+ fi
+
+ echo "Successfully retrieved values from azd environment."
+ return 0
+}
+
+# Helper function to extract value with fallback
+extract_value() {
+ local primary_key="$1"
+ local fallback_key="$2"
+ local result
+
+ result=$(echo "$deploymentOutputs" | grep -A 3 "\"$primary_key\"" | grep '"value"' | sed 's/.*"value": *"\([^"]*\)".*/\1/')
+ if [ -z "$result" ]; then
+ result=$(echo "$deploymentOutputs" | grep -A 3 "\"$fallback_key\"" | grep '"value"' | sed 's/.*"value": *"\([^"]*\)".*/\1/')
+ fi
+ echo "$result"
+}
+
+function get_values_from_az_deployment() {
+ echo "Getting values from Azure deployment outputs..."
+
+ directoryPath="data/agent_teams"
+
+ echo "Fetching deployment name..."
+ deploymentName=$(az group show --name "$ResourceGroup" --query "tags.DeploymentName" -o tsv)
+ if [[ -z "$deploymentName" ]]; then
+ echo "Error: Could not find deployment name in resource group tags."
+ return 1
+ fi
+
+ echo "Fetching deployment outputs for deployment: $deploymentName"
+ deploymentOutputs=$(az deployment group show --resource-group "$ResourceGroup" --name "$deploymentName" --query "properties.outputs" -o json)
+ if [[ -z "$deploymentOutputs" ]]; then
+ echo "Error: Could not fetch deployment outputs."
+ return 1
+ fi
+
+ # Extract all values using the helper function
+ storageAccount=$(extract_value "azurE_STORAGE_ACCOUNT_NAME" "azureStorageAccountName")
+ blobContainerForRetailCustomer=$(extract_value "azurE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER" "azureStorageContainerNameRetailCustomer")
+ blobContainerForRetailOrder=$(extract_value "azurE_STORAGE_CONTAINER_NAME_RETAIL_ORDER" "azureStorageContainerNameRetailOrder")
+ blobContainerForRFPSummary=$(extract_value "azurE_STORAGE_CONTAINER_NAME_RFP_SUMMARY" "azureStorageContainerNameRfpSummary")
+ blobContainerForRFPRisk=$(extract_value "azurE_STORAGE_CONTAINER_NAME_RFP_RISK" "azureStorageContainerNameRfpRisk")
+ blobContainerForRFPCompliance=$(extract_value "azurE_STORAGE_CONTAINER_NAME_RFP_COMPLIANCE" "azureStorageContainerNameRfpCompliance")
+ blobContainerForContractSummary=$(extract_value "azurE_STORAGE_CONTAINER_NAME_CONTRACT_SUMMARY" "azureStorageContainerNameContractSummary")
+ blobContainerForContractRisk=$(extract_value "azurE_STORAGE_CONTAINER_NAME_CONTRACT_RISK" "azureStorageContainerNameContractRisk")
+ blobContainerForContractCompliance=$(extract_value "azurE_STORAGE_CONTAINER_NAME_CONTRACT_COMPLIANCE" "azureStorageContainerNameContractCompliance")
+ aiSearchIndexForRetailCustomer=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_RETAIL_CUSTOMER" "azureAiSearchIndexNameRetailCustomer")
+ aiSearchIndexForRetailOrder=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_RETAIL_ORDER" "azureAiSearchIndexNameRetailOrder")
+ aiSearchIndexForRFPSummary=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_RFP_SUMMARY" "azureAiSearchIndexNameRfpSummary")
+ aiSearchIndexForRFPRisk=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_RFP_RISK" "azureAiSearchIndexNameRfpRisk")
+ aiSearchIndexForRFPCompliance=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_RFP_COMPLIANCE" "azureAiSearchIndexNameRfpCompliance")
+ aiSearchIndexForContractSummary=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_CONTRACT_SUMMARY" "azureAiSearchIndexNameContractSummary")
+ aiSearchIndexForContractRisk=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_CONTRACT_RISK" "azureAiSearchIndexNameContractRisk")
+ aiSearchIndexForContractCompliance=$(extract_value "azurE_AI_SEARCH_INDEX_NAME_CONTRACT_COMPLIANCE" "azureAiSearchIndexNameContractCompliance")
+ aiSearch=$(extract_value "azurE_AI_SEARCH_NAME" "azureAiSearchName")
+ backendUrl=$(extract_value "backenD_URL" "backendUrl")
+
+ # Validate that we extracted all required values
+ if [[ -z "$storageAccount" || -z "$aiSearch" || -z "$backendUrl" ]]; then
+ echo "Error: Could not extract all required values from deployment outputs."
+ return 1
+ fi
+
+ echo "Successfully retrieved values from deployment outputs."
+ return 0
+}
+
+function get_values_using_solution_suffix() {
+ echo "Getting values from resource naming convention using solution suffix..."
+
+ # Get the solution suffix from resource group tags
+ solutionSuffix=$(az group show --name "$ResourceGroup" --query "tags.SolutionSuffix" -o tsv)
+ if [[ -z "$solutionSuffix" ]]; then
+ echo "Error: Could not find SolutionSuffix tag in resource group."
+ return 1
+ fi
+
+ echo "Found solution suffix: $solutionSuffix"
+
+ # Reconstruct resource names using same naming convention as Bicep
+ storageAccount=$(echo "st${solutionSuffix}" | tr -d '-') # Remove dashes like Bicep does
+ aiSearch="srch-${solutionSuffix}"
+ containerAppName="ca-${solutionSuffix}"
+
+ # Query dynamic value (backend URL) from Container App
+ echo "Querying backend URL from Container App..."
+ backendFqdn=$(az containerapp show \
+ --name "$containerAppName" \
+ --resource-group "$ResourceGroup" \
+ --query "properties.configuration.ingress.fqdn" \
+ -o tsv 2>/dev/null)
+
+ if [[ -z "$backendFqdn" ]]; then
+ echo "Error: Could not get Container App FQDN. Container App may not be deployed yet."
+ return 1
+ fi
+
+ backendUrl="https://${backendFqdn}"
+
+ # Hardcoded container names (These don't follow the suffix pattern in Bicep, hence need to be changed here if changed in Bicep)
+ blobContainerForRetailCustomer="retail-dataset-customer"
+ blobContainerForRetailOrder="retail-dataset-order"
+ blobContainerForRFPSummary="rfp-summary-dataset"
+ blobContainerForRFPRisk="rfp-risk-dataset"
+ blobContainerForRFPCompliance="rfp-compliance-dataset"
+ blobContainerForContractSummary="contract-summary-dataset"
+ blobContainerForContractRisk="contract-risk-dataset"
+ blobContainerForContractCompliance="contract-compliance-dataset"
+
+ # Hardcoded index names (These don't follow the suffix pattern in Bicep, hence need to be changed here if changed in Bicep)
+ aiSearchIndexForRetailCustomer="macae-retail-customer-index"
+ aiSearchIndexForRetailOrder="macae-retail-order-index"
+ aiSearchIndexForRFPSummary="macae-rfp-summary-index"
+ aiSearchIndexForRFPRisk="macae-rfp-risk-index"
+ aiSearchIndexForRFPCompliance="macae-rfp-compliance-index"
+ aiSearchIndexForContractSummary="contract-summary-doc-index"
+ aiSearchIndexForContractRisk="contract-risk-doc-index"
+ aiSearchIndexForContractCompliance="contract-compliance-doc-index"
+
+ directoryPath="data/agent_teams"
+
+ # Validate that we got all critical values
+ if [[ -z "$storageAccount" || -z "$aiSearch" || -z "$backendUrl" ]]; then
+ echo "Error: Failed to reconstruct all required resource names."
+ return 1
+ fi
+
+ echo "Successfully reconstructed values from resource naming convention."
+ return 0
+}
+
+# Authenticate with Azure
+if az account show &> /dev/null; then
+ echo "Already authenticated with Azure."
+else
+ echo "Not authenticated with Azure. Attempting to authenticate..."
+ echo "Authenticating with Azure CLI..."
+ az login
+fi
+
+# Get subscription ID from azd if available
+if test_azd_installed; then
+ azSubscriptionId=$(azd env get-value AZURE_SUBSCRIPTION_ID 2>/dev/null || echo "")
+ if [[ -z "$azSubscriptionId" ]]; then
+ azSubscriptionId="$AZURE_SUBSCRIPTION_ID"
+ fi
+fi
+
+# Check if user has selected the correct subscription
+currentSubscriptionId=$(az account show --query id -o tsv)
+currentSubscriptionName=$(az account show --query name -o tsv)
+
+if [[ "$currentSubscriptionId" != "$azSubscriptionId" && -n "$azSubscriptionId" ]]; then
+ echo "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
+ read -p "Do you want to continue with this subscription?(y/n): " confirmation
+ if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then
+ echo "Fetching available subscriptions..."
+ availableSubscriptions=$(az account list --query "[?state=='Enabled'].[name,id]" --output tsv)
+
+ # Convert to array
+ IFS=$'\n' read -d '' -r -a subscriptions <<< "$availableSubscriptions"
+
+ while true; do
+ echo ""
+ echo "Available Subscriptions:"
+ echo "========================"
+ local index=1
+ for ((i=0; i<${#subscriptions[@]}; i++)); do
+ IFS=$'\t' read -r name id <<< "${subscriptions[i]}"
+ echo "$index. $name ( $id )"
+ ((index++))
+ done
+ echo "========================"
+ echo ""
+
+ read -p "Enter the number of the subscription (1-$((${#subscriptions[@]}))) to use: " subscriptionIndex
+
+ if [[ "$subscriptionIndex" =~ ^[0-9]+$ ]] && [[ "$subscriptionIndex" -ge 1 ]] && [[ "$subscriptionIndex" -le "${#subscriptions[@]}" ]]; then
+ selectedIndex=$((subscriptionIndex - 1))
+ IFS=$'\t' read -r selectedSubscriptionName selectedSubscriptionId <<< "${subscriptions[selectedIndex]}"
+
+ if az account set --subscription "$selectedSubscriptionId"; then
+ echo "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
+ azSubscriptionId="$selectedSubscriptionId"
+ break
+ else
+ echo "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
+ fi
+ else
+ echo "Invalid selection. Please try again."
+ fi
+ done
+ else
+ echo "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
+ az account set --subscription "$currentSubscriptionId"
+ azSubscriptionId="$currentSubscriptionId"
+ fi
+else
+ echo "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
+ az account set --subscription "$currentSubscriptionId"
+ azSubscriptionId="$currentSubscriptionId"
+fi
+
+# Get configuration values based on strategy
+if [[ -z "$ResourceGroup" ]]; then
+ # No resource group provided - use azd env
+ if ! get_values_from_azd_env; then
+ echo "Failed to get values from azd environment."
+ echo "If you want to use deployment outputs instead, please provide the resource group name as an argument."
+ echo "Usage: ./selecting-team-config-and-data.sh --resource-group "
+ exit 1
+ fi
+else
+ # Resource group provided - use deployment outputs, then fallback to naming convention
+ echo "Resource group provided: $ResourceGroup"
+
+ if ! get_values_from_az_deployment; then
+ echo ""
+ echo "Warning: Could not retrieve values from deployment outputs (deployment may be deleted)."
+ echo "Attempting fallback method: reconstructing values from resource naming convention..."
+ echo ""
+
+ if ! get_values_using_solution_suffix; then
+ echo ""
+ echo "Error: Both methods failed to retrieve configuration values."
+ echo "Please ensure:"
+ echo " 1. The deployment exists and has a DeploymentName tag, OR"
+ echo " 2. The resource group has a SolutionSuffix tag"
+ exit 1
+ fi
+ fi
+fi
+
+# Interactive Use Case Selection
+echo ""
+echo "==============================================="
+echo "Available Use Cases:"
+echo "==============================================="
+echo "1. RFP Evaluation"
+echo "2. Retail Customer Satisfaction"
+echo "3. HR Employee Onboarding"
+echo "4. Marketing Press Release"
+echo "5. Contract Compliance Review"
+echo "6. All"
+echo "==============================================="
+echo ""
+
+# Prompt user for use case selection
+useCaseValid=false
+while [[ "$useCaseValid" != true ]]; do
+ read -p "Please enter the number of the use case you would like to install: " useCaseSelection
+
+ # Handle both numeric and text input for 'all'
+ if [[ "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ selectedUseCase="All"
+ useCaseValid=true
+ echo "Selected: All use cases will be installed."
+ elif [[ "$useCaseSelection" == "1" ]]; then
+ selectedUseCase="RFP Evaluation"
+ useCaseValid=true
+ echo "Selected: RFP Evaluation"
+ echo "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ elif [[ "$useCaseSelection" == "2" ]]; then
+ selectedUseCase="Retail Customer Satisfaction"
+ useCaseValid=true
+ echo "Selected: Retail Customer Satisfaction"
+ echo "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ elif [[ "$useCaseSelection" == "3" ]]; then
+ selectedUseCase="HR Employee Onboarding"
+ useCaseValid=true
+ echo "Selected: HR Employee Onboarding"
+ echo "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ elif [[ "$useCaseSelection" == "4" ]]; then
+ selectedUseCase="Marketing Press Release"
+ useCaseValid=true
+ echo "Selected: Marketing Press Release"
+ echo "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ elif [[ "$useCaseSelection" == "5" ]]; then
+ selectedUseCase="Contract Compliance Review"
+ useCaseValid=true
+ echo "Selected: Contract Compliance Review"
+ echo "Note: If you choose to install a single use case, installation of other use cases will require re-running this script."
+ else
+ useCaseValid=false
+ echo -e "\033[31mInvalid selection. Please enter a number from 1-6.\033[0m"
+ fi
+done
+
+echo ""
+echo "==============================================="
+echo "Values to be used:"
+echo "==============================================="
+echo "Selected Use Case: $selectedUseCase"
+echo "Resource Group: $ResourceGroup"
+echo "Backend URL: $backendUrl"
+echo "Storage Account: $storageAccount"
+echo "AI Search: $aiSearch"
+echo "Directory Path: $directoryPath"
+echo "Subscription ID: $azSubscriptionId"
+echo "==============================================="
+echo ""
+
+userPrincipalId=$(az ad signed-in-user show --query id -o tsv)
+
+# Determine the correct Python command
+pythonCmd=""
+
+if command -v python &> /dev/null; then
+ pythonVersion=$(python --version 2>&1)
+ if [[ "$pythonVersion" =~ Python\ [0-9] ]]; then
+ pythonCmd="python"
+ fi
+fi
+
+if [[ -z "$pythonCmd" ]]; then
+ if command -v python3 &> /dev/null; then
+ pythonVersion=$(python3 --version 2>&1)
+ if [[ "$pythonVersion" =~ Python\ [0-9] ]]; then
+ pythonCmd="python3"
+ fi
+ fi
+fi
+
+if [[ -z "$pythonCmd" ]]; then
+ echo "Python is not installed on this system or it is not added in the PATH."
+ exit 1
+fi
+
+# Create virtual environment
+venvPath="infra/scripts/scriptenv"
+if [[ -d "$venvPath" ]]; then
+ echo "Virtual environment already exists. Skipping creation."
+else
+ echo "Creating virtual environment"
+ $pythonCmd -m venv "$venvPath"
+fi
+
+# Activate the virtual environment
+if [[ -f "$venvPath/bin/activate" ]]; then
+ echo "Activating virtual environment"
+ source "$venvPath/bin/activate"
+elif [[ -f "$venvPath/Scripts/activate" ]]; then
+ echo "Activating virtual environment"
+ source "$venvPath/Scripts/activate"
+else
+ echo "Error activating virtual environment. Requirements may be installed globally."
+fi
+
+# Install the requirements
+echo "Installing requirements"
+pip install --quiet -r infra/scripts/requirements.txt
+echo "Requirements installed"
+
+isTeamConfigFailed=false
+isSampleDataFailed=false
+failedTeamConfigs=0
+
+# Use Case 3 - HR Employee Onboarding
+if [[ "$useCaseSelection" == "3" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ echo "Uploading Team Configuration for HR Employee Onboarding..."
+ directoryPath="data/agent_teams"
+ teamId="00000000-0000-0000-0000-000000000001"
+
+ if $pythonCmd infra/scripts/upload_team_config.py "$backendUrl" "$directoryPath" "$userPrincipalId" "$teamId"; then
+ echo "Successfully uploaded team configuration for HR Employee Onboarding."
+ else
+ echo "Error: Team configuration for HR Employee Onboarding upload failed."
+ isTeamConfigFailed=true
+ ((failedTeamConfigs++))
+ fi
+fi
+
+# Use Case 4 - Marketing Press Release
+if [[ "$useCaseSelection" == "4" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ echo "Uploading Team Configuration for Marketing Press Release..."
+ directoryPath="data/agent_teams"
+ teamId="00000000-0000-0000-0000-000000000002"
+
+ if $pythonCmd infra/scripts/upload_team_config.py "$backendUrl" "$directoryPath" "$userPrincipalId" "$teamId"; then
+ echo "Successfully uploaded team configuration for Marketing Press Release."
+ else
+ echo "Error: Team configuration for Marketing Press Release upload failed."
+ isTeamConfigFailed=true
+ ((failedTeamConfigs++))
+ fi
+fi
+
+# Enable public access for resources
+if [[ "$useCaseSelection" == "1" || "$useCaseSelection" == "2" || "$useCaseSelection" == "5" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ if [[ -n "$ResourceGroup" ]]; then
+ # Check if resource group has Type=WAF tag
+ rgTypeTag=$(az group show --name "$ResourceGroup" --query "tags.Type" -o tsv 2>/dev/null)
+
+ if [[ "$rgTypeTag" == "WAF" ]]; then
+ echo ""
+ echo "=== Temporarily enabling public network access for services ==="
+ stPublicAccess=$(az storage account show --name "$storageAccount" --resource-group "$ResourceGroup" --query "publicNetworkAccess" -o tsv)
+ if [[ "$stPublicAccess" == "Disabled" ]]; then
+ stIsPublicAccessDisabled=true
+ echo "Enabling public access for Storage Account: $storageAccount"
+ az storage account update --name "$storageAccount" --public-network-access enabled --default-action Allow --output none
+ if [[ $? -ne 0 ]]; then
+ echo "Error: Failed to enable public access for storage account."
+ exit 1
+ fi
+
+ # Wait 30 seconds for the change to propagate
+ echo "Waiting 30 seconds for public access to be enabled..."
+ sleep 30
+
+ # Verify public access is enabled in a loop
+ echo "Verifying public access is enabled..."
+ maxRetries=5
+ retryCount=0
+ while [[ $retryCount -lt $maxRetries ]]; do
+ currentAccess=$(az storage account show --name "$storageAccount" --resource-group "$ResourceGroup" --query "publicNetworkAccess" -o tsv)
+ if [[ "$currentAccess" == "Enabled" ]]; then
+ echo "โ Storage Account public access enabled successfully"
+ break
+ else
+ echo "Public access not yet enabled (attempt $((retryCount + 1))/$maxRetries). Waiting 5 seconds..."
+ sleep 5
+ ((retryCount++))
+ fi
+ done
+
+ if [[ $retryCount -eq $maxRetries ]]; then
+ echo "Warning: Public access verification timed out for storage account."
+ fi
+ else
+ echo "โ Storage Account public access already enabled"
+ fi
+ fi
+
+ if [[ "$rgTypeTag" == "WAF" ]]; then
+ srchPublicAccess=$(az search service show --name "$aiSearch" --resource-group "$ResourceGroup" --query "publicNetworkAccess" -o tsv)
+ if [[ "$srchPublicAccess" == "Disabled" ]]; then
+ srchIsPublicAccessDisabled=true
+ echo "Enabling public access for AI Search Service: $aiSearch"
+ az search service update --name "$aiSearch" --resource-group "$ResourceGroup" --public-network-access enabled --output none
+ if [[ $? -ne 0 ]]; then
+ echo "Error: Failed to enable public access for search service."
+ exit 1
+ fi
+
+ # Wait 30 seconds for the change to propagate
+ echo "Waiting 30 seconds for public access to be enabled..."
+ sleep 30
+
+ # Verify public access is enabled in a loop
+ echo "Verifying public access is enabled..."
+ maxRetries=5
+ retryCount=0
+ while [[ $retryCount -lt $maxRetries ]]; do
+ currentAccess=$(az search service show --name "$aiSearch" --resource-group "$ResourceGroup" --query "publicNetworkAccess" -o tsv)
+ if [[ "$currentAccess" == "Enabled" ]]; then
+ echo "โ AI Search Service public access enabled successfully"
+ break
+ else
+ echo "Public access not yet enabled (attempt $((retryCount + 1))/$maxRetries). Waiting 5 seconds..."
+ sleep 5
+ ((retryCount++))
+ fi
+ done
+
+ if [[ $retryCount -eq $maxRetries ]]; then
+ echo "Warning: Public access verification timed out for search service."
+ fi
+ else
+ echo "โ AI Search Service public access already enabled"
+ fi
+ echo "==========================================================="
+ echo ""
+ fi
+ fi
+fi
+
+# Use Case 1 - RFP Evaluation
+if [[ "$useCaseSelection" == "1" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ echo "Uploading Team Configuration for RFP Evaluation..."
+ directoryPath="data/agent_teams"
+ teamId="00000000-0000-0000-0000-000000000004"
+
+ if $pythonCmd infra/scripts/upload_team_config.py "$backendUrl" "$directoryPath" "$userPrincipalId" "$teamId"; then
+ echo "Uploaded Team Configuration for RFP Evaluation..."
+ else
+ echo "Error: Team configuration for RFP Evaluation upload failed."
+ ((failedTeamConfigs++))
+ isTeamConfigFailed=true
+ fi
+
+ directoryPath="data/datasets/rfp/summary"
+ # Upload sample files to blob storage
+ echo "Uploading sample files to blob storage for RFP Evaluation..."
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainerForRFPSummary" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+
+ directoryPath="data/datasets/rfp/risk"
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainerForRFPRisk" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+
+ directoryPath="data/datasets/rfp/compliance"
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainerForRFPCompliance" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+ echo "Files uploaded successfully to blob storage."
+
+ # Run the Python script to index data
+ echo "Running the python script to index data for RFP Evaluation"
+ if $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "$blobContainerForRFPSummary" "$aiSearch" "$aiSearchIndexForRFPSummary"; then
+ echo "Python script to index data for RFP Summary successfully executed."
+ else
+ echo "Error: Indexing python script execution failed for RFP Summary."
+ isSampleDataFailed=true
+ fi
+
+ if $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "$blobContainerForRFPRisk" "$aiSearch" "$aiSearchIndexForRFPRisk"; then
+ echo "Python script to index data for RFP Risk successfully executed."
+ else
+ echo "Error: Indexing python script execution failed for RFP Risk."
+ isSampleDataFailed=true
+ fi
+
+ if $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "$blobContainerForRFPCompliance" "$aiSearch" "$aiSearchIndexForRFPCompliance"; then
+ echo "Python script to index data for RFP Compliance successfully executed."
+ else
+ echo "Error: Indexing python script execution failed for RFP Compliance."
+ isSampleDataFailed=true
+ fi
+ echo "Python script to index data for RFP Evaluation successfully executed."
+fi
+
+# Use Case 5 - Contract Compliance Review
+if [[ "$useCaseSelection" == "5" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ echo "Uploading Team Configuration for Contract Compliance Review..."
+ directoryPath="data/agent_teams"
+ teamId="00000000-0000-0000-0000-000000000005"
+
+ if $pythonCmd infra/scripts/upload_team_config.py "$backendUrl" "$directoryPath" "$userPrincipalId" "$teamId"; then
+ echo "Uploaded Team Configuration for Contract Compliance Review..."
+ else
+ echo "Error: Team configuration for Contract Compliance Review upload failed."
+ ((failedTeamConfigs++))
+ isTeamConfigFailed=true
+ fi
+
+ directoryPath="data/datasets/contract_compliance/summary"
+ # Upload sample files to blob storage
+ echo "Uploading sample files to blob storage for Contract Compliance Review..."
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainerForContractSummary" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+
+ directoryPath="data/datasets/contract_compliance/risk"
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainerForContractRisk" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+
+ directoryPath="data/datasets/contract_compliance/compliance"
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainerForContractCompliance" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+ echo "Files uploaded successfully to blob storage."
+
+ # Run the Python script to index data
+ echo "Running the python script to index data for Contract Compliance Review"
+ if $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "$blobContainerForContractSummary" "$aiSearch" "$aiSearchIndexForContractSummary"; then
+ echo "Python script to index data for Contract Summary successfully executed."
+ else
+ echo "Error: Indexing python script execution failed for Contract Summary."
+ isSampleDataFailed=true
+ fi
+
+ if $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "$blobContainerForContractRisk" "$aiSearch" "$aiSearchIndexForContractRisk"; then
+ echo "Python script to index data for Contract Risk successfully executed."
+ else
+ echo "Error: Indexing python script execution failed for Contract Risk."
+ isSampleDataFailed=true
+ fi
+
+ if $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "$blobContainerForContractCompliance" "$aiSearch" "$aiSearchIndexForContractCompliance"; then
+ echo "Python script to index data for Contract Compliance successfully executed."
+ else
+ echo "Error: Indexing python script execution failed for Contract Compliance."
+ isSampleDataFailed=true
+ fi
+ echo "Python script to index data for Contract Compliance Review successfully executed."
+fi
+
+# Use Case 2 - Retail Customer Satisfaction
+if [[ "$useCaseSelection" == "2" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ echo "Uploading Team Configuration for Retail Customer Satisfaction..."
+ directoryPath="data/agent_teams"
+ teamId="00000000-0000-0000-0000-000000000003"
+
+ if $pythonCmd infra/scripts/upload_team_config.py "$backendUrl" "$directoryPath" "$userPrincipalId" "$teamId"; then
+ echo "Uploaded Team Configuration for Retail Customer Satisfaction..."
+ else
+ echo "Error: Team configuration for Retail Customer Satisfaction upload failed."
+ ((failedTeamConfigs++))
+ isTeamConfigFailed=true
+ fi
+
+ directoryPath="data/datasets/retail/customer"
+ # Upload sample files to blob storage
+ echo "Uploading sample files to blob storage for Retail Customer Satisfaction..."
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "retail-dataset-customer" --source "$directoryPath" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+
+ directoryPath="data/datasets/retail/order"
+ if ! az storage blob upload-batch --account-name "$storageAccount" --destination "retail-dataset-order" --source "data/datasets/retail/order" --auth-mode login --pattern "*" --overwrite --output none; then
+ echo "Error: Failed to upload files to blob storage."
+ isSampleDataFailed=true
+ exit 1
+ fi
+ echo "Files uploaded successfully to blob storage."
+
+ # Run the Python script to index data
+ echo "Running the python script to index data for Retail Customer Satisfaction"
+ if ! $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "retail-dataset-customer" "$aiSearch" "macae-retail-customer-index"; then
+ echo "Error: Indexing python script execution failed."
+ isSampleDataFailed=true
+ exit 1
+ fi
+
+ if ! $pythonCmd infra/scripts/index_datasets.py "$storageAccount" "retail-dataset-order" "$aiSearch" "macae-retail-order-index"; then
+ echo "Error: Indexing python script execution failed."
+ isSampleDataFailed=true
+ exit 1
+ fi
+ echo "Python script to index data for Retail Customer Satisfaction successfully executed."
+fi
+
+
+if [[ "$isTeamConfigFailed" == true || "$isSampleDataFailed" == true ]]; then
+ echo ""
+ echo "One or more tasks failed. Please check the error messages above."
+ exit 1
+else
+ if [[ "$useCaseSelection" == "1" || "$useCaseSelection" == "2" || "$useCaseSelection" == "5" || "$useCaseSelection" == "all" || "$useCaseSelection" == "6" ]]; then
+ echo ""
+ echo "Team configuration upload and sample data processing completed successfully."
+ else
+ echo ""
+ echo "Team configuration upload completed successfully."
+ fi
+fi
\ No newline at end of file
diff --git a/infra/scripts/team_config_and_data.sh b/infra/scripts/team_config_and_data.sh
deleted file mode 100644
index d7aaf214a..000000000
--- a/infra/scripts/team_config_and_data.sh
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/bin/bash
-
-# Variables
-resourceGroup="$1"
-
-directoryPath=""
-backendUrl=""
-storageAccount=""
-blobContainer=""
-aiSearch=""
-aiSearchIndex=""
-azSubscriptionId=""
-
-# check if azd cli is installed
-check_azd_installed() {
- if command -v azd &> /dev/null; then
- return 0
- else
- return 1
- fi
-}
-
-get_values_from_azd_env() {
- check_azd_installed
- if [ $? -ne 0 ]; then
- echo "Error: Azure Developer CLI is not installed."
- return 1
- fi
-
- echo "Getting values from azd environment..."
-
- directoryPath="data/agent_teams"
- backendUrl=$(azd env get-value BACKEND_URL)
- storageAccount=$(azd env get-value AZURE_STORAGE_ACCOUNT_NAME)
- blobContainer=$(azd env get-value AZURE_STORAGE_CONTAINER_NAME)
- aiSearch=$(azd env get-value AZURE_AI_SEARCH_NAME)
- aiSearchIndex=$(azd env get-value AZURE_AI_SEARCH_INDEX_NAME)
- resourceGroup=$(azd env get-value AZURE_RESOURCE_GROUP)
-
- # Validate that we got all required values
- if [ -z "$backendUrl" ] || [ -z "$storageAccount" ] || [ -z "$blobContainer" ] || [ -z "$aiSearch" ] || [ -z "$aiSearchIndex" ] || [ -z "$resourceGroup" ]; then
- echo "Error: Could not retrieve all required values from azd environment."
- return 1
- fi
-
- echo "Successfully retrieved values from azd environment."
- return 0
-}
-
-# Helper function to extract value with fallback
-extract_value() {
- local primary_key="$1"
- local fallback_key="$2"
- local result
-
- result=$(echo "$deploymentOutputs" | grep -A 3 "\"$primary_key\"" | grep '"value"' | sed 's/.*"value": *"\([^"]*\)".*/\1/')
- if [ -z "$result" ]; then
- result=$(echo "$deploymentOutputs" | grep -A 3 "\"$fallback_key\"" | grep '"value"' | sed 's/.*"value": *"\([^"]*\)".*/\1/')
- fi
- echo "$result"
-}
-
-get_values_from_az_deployment() {
- echo "Getting values from Azure deployment outputs..."
-
- directoryPath="data/agent_teams"
-
- echo "Fetching deployment name..."
- deploymentName=$(az group show --name "$resourceGroup" --query "tags.DeploymentName" -o tsv)
- if [ -z "$deploymentName" ]; then
- echo "Error: Could not find deployment name in resource group tags."
- return 1
- fi
-
- echo "Fetching deployment outputs for deployment: $deploymentName"
- deploymentOutputs=$(az deployment group show --resource-group "$resourceGroup" --name "$deploymentName" --query "properties.outputs" -o json)
- if [ -z "$deploymentOutputs" ]; then
- echo "Error: Could not fetch deployment outputs."
- return 1
- fi
-
- # Extract all values using the helper function
- storageAccount=$(extract_value "azurE_STORAGE_ACCOUNT_NAME" "azureStorageAccountName")
- blobContainer=$(extract_value "azurE_STORAGE_CONTAINER_NAME" "azureStorageContainerName")
- aiSearch=$(extract_value "azurE_AI_SEARCH_NAME" "azureAiSearchName")
- aiSearchIndex=$(extract_value "azurE_AI_SEARCH_INDEX_NAME" "azureAiSearchIndexName")
- backendUrl=$(extract_value "backenD_URL" "backendUrl")
-
- # Validate that we extracted all required values
- if [ -z "$storageAccount" ] || [ -z "$blobContainer" ] || [ -z "$aiSearch" ] || [ -z "$aiSearchIndex" ] || [ -z "$backendUrl" ]; then
- echo "Error: Could not extract all required values from deployment outputs."
- return 1
- fi
-
- echo "Successfully retrieved values from deployment outputs."
- return 0
-}
-
-# Authenticate with Azure
-if az account show &> /dev/null; then
- echo "Already authenticated with Azure."
-else
- echo "Not authenticated with Azure. Attempting to authenticate..."
- echo "Authenticating with Azure CLI..."
- az login
-fi
-
-if check_azd_installed; then
- azSubscriptionId=$(azd env get-value AZURE_SUBSCRIPTION_ID) || azSubscriptionId="$AZURE_SUBSCRIPTION_ID" || azSubscriptionId=""
-fi
-
-#check if user has selected the correct subscription
-currentSubscriptionId=$(az account show --query id -o tsv)
-currentSubscriptionName=$(az account show --query name -o tsv)
-if [ "$currentSubscriptionId" != "$azSubscriptionId" ]; then
- echo "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
- read -rp "Do you want to continue with this subscription?(y/n): " confirmation
- if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then
- echo "Fetching available subscriptions..."
- availableSubscriptions=$(az account list --query "[?state=='Enabled'].[name,id]" --output tsv)
- while true; do
- echo ""
- echo "Available Subscriptions:"
- echo "========================"
- echo "$availableSubscriptions" | awk '{printf "%d. %s ( %s )\n", NR, $1, $2}'
- echo "========================"
- echo ""
- read -rp "Enter the number of the subscription (1-$(echo "$availableSubscriptions" | wc -l)) to use: " subscriptionIndex
- if [[ "$subscriptionIndex" =~ ^[0-9]+$ ]] && [ "$subscriptionIndex" -ge 1 ] && [ "$subscriptionIndex" -le $(echo "$availableSubscriptions" | wc -l) ]; then
- selectedSubscription=$(echo "$availableSubscriptions" | sed -n "${subscriptionIndex}p")
- selectedSubscriptionName=$(echo "$selectedSubscription" | cut -f1)
- selectedSubscriptionId=$(echo "$selectedSubscription" | cut -f2)
-
- # Set the selected subscription
- if az account set --subscription "$selectedSubscriptionId"; then
- echo "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
- azSubscriptionId="$selectedSubscriptionId"
- break
- else
- echo "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
- fi
- else
- echo "Invalid selection. Please try again."
- fi
- done
- else
- echo "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription "$currentSubscriptionId"
- azSubscriptionId="$currentSubscriptionId"
- fi
-else
- echo "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription "$currentSubscriptionId"
- azSubscriptionId="$currentSubscriptionId"
-fi
-
-
-if [ -z "$resourceGroup" ]; then
- # No resource group provided - use azd env
- if ! get_values_from_azd_env; then
- echo "Failed to get values from azd environment."
- echo "If you want to use deployment outputs instead, please provide the resource group name as an argument."
- echo "Usage: $0 [ResourceGroupName]"
- exit 1
- fi
-else
- # Resource group provided - use deployment outputs
- echo "Resource group provided: $resourceGroup"
-
- # Call deployment function
- if ! get_values_from_az_deployment; then
- echo "Failed to get values from deployment outputs."
- exit 1
- fi
-fi
-
-echo ""
-echo "==============================================="
-echo "Values to be used:"
-echo "==============================================="
-echo "Resource Group: $resourceGroup"
-echo "Backend URL: $backendUrl"
-echo "Storage Account: $storageAccount"
-echo "Blob Container: $blobContainer"
-echo "AI Search: $aiSearch"
-echo "AI Search Index: $aiSearchIndex"
-echo "Directory Path: $directoryPath"
-echo "Subscription ID: $azSubscriptionId"
-echo "==============================================="
-echo ""
-
-isTeamConfigFailed=false
-isSampleDataFailed=false
-
-echo "Uploading team configuration..."
-bash infra/scripts/upload_team_config.sh "$backendUrl" "$directoryPath" "$azSubscriptionId"
-if [ $? -ne 0 ]; then
- echo "Error: Team configuration upload failed."
- isTeamConfigFailed=true
-fi
-
-echo ""
-echo "----------------------------------------"
-echo "----------------------------------------"
-echo ""
-
-echo "Processing sample data..."
-bash infra/scripts/process_sample_data.sh "$storageAccount" "$blobContainer" "$aiSearch" "$aiSearchIndex" "$resourceGroup" "$azSubscriptionId"
-if [ $? -ne 0 ]; then
- echo "Error: Sample data processing failed."
- isSampleDataFailed=true
-fi
-
-if [ "$isTeamConfigFailed" = true ] || [ "$isSampleDataFailed" = true ]; then
- echo "One or more processes failed."
- exit 1
-fi
-
-echo "Both team configuration upload and sample data processing completed successfully."
\ No newline at end of file
diff --git a/infra/scripts/upload_team_config.py b/infra/scripts/upload_team_config.py
index d3c2b45e2..bb44cd166 100644
--- a/infra/scripts/upload_team_config.py
+++ b/infra/scripts/upload_team_config.py
@@ -15,7 +15,7 @@ def check_team_exists(backend_url, team_id, user_principal_id):
Returns:
exists: bool
"""
- check_endpoint = backend_url.rstrip('/') + f'/api/v3/team_configs/{team_id}'
+ check_endpoint = backend_url.rstrip('/') + f'/api/v4/team_configs/{team_id}'
headers = {
'x-ms-client-principal-id': user_principal_id
}
@@ -33,13 +33,14 @@ def check_team_exists(backend_url, team_id, user_principal_id):
print(f"Exception checking team {team_id}: {str(e)}")
return False
-if len(sys.argv) < 2:
- print("Usage: python upload_team_config.py []")
+if len(sys.argv) < 3:
+ print("Usage: python upload_team_config.py [] []")
sys.exit(1)
backend_url = sys.argv[1]
directory_path = sys.argv[2]
user_principal_id = sys.argv[3] if len(sys.argv) > 3 and sys.argv[3].strip() != "" else "00000000-0000-0000-0000-000000000000"
+team_id_from_arg = sys.argv[4] if len(sys.argv) > 4 else "00000000-0000-0000-0000-000000000001"
# Convert to absolute path if provided as relative
directory_path = os.path.abspath(directory_path)
@@ -49,62 +50,68 @@ def check_team_exists(backend_url, team_id, user_principal_id):
("hr.json", "00000000-0000-0000-0000-000000000001"),
("marketing.json", "00000000-0000-0000-0000-000000000002"),
("retail.json", "00000000-0000-0000-0000-000000000003"),
+ ("rfp_analysis_team.json", "00000000-0000-0000-0000-000000000004"),
+ ("contract_compliance_team.json", "00000000-0000-0000-0000-000000000005"),
]
-upload_endpoint = backend_url.rstrip('/') + '/api/v3/upload_team_config'
+upload_endpoint = backend_url.rstrip('/') + '/api/v4/upload_team_config'
# Process each JSON file in the directory
uploaded_count = 0
for filename, team_id in files_to_process:
- file_path = os.path.join(directory_path, filename)
- if os.path.isfile(file_path):
- print(f"Uploading file: {filename}")
- # Check if team already exists
- team_exists = check_team_exists(backend_url, team_id, user_principal_id)
- if team_exists:
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- team_data = json.load(f)
- team_name = team_data.get('name', 'Unknown')
- print(f"Team '{team_name}' (ID: {team_id}) already exists!")
+ if team_id == team_id_from_arg:
+ file_path = os.path.join(directory_path, filename)
+ if os.path.isfile(file_path):
+ print(f"Uploading file: {filename}")
+ team_exists = check_team_exists(backend_url, team_id, user_principal_id)
+ if team_exists:
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ team_data = json.load(f)
+ team_name = team_data.get('name', 'Unknown')
+ print(f"Team '{team_name}' (ID: {team_id}) already exists!")
+ continue
+ except Exception as e:
+ print(f"Error reading {filename}: {str(e)}")
continue
- except Exception as e:
- print(f"Error reading {filename}: {str(e)}")
- continue
-
- try:
- with open(file_path, 'rb') as file_data:
- files = {
- 'file': (filename, file_data, 'application/json')
- }
- headers = {
- 'x-ms-client-principal-id': user_principal_id
- }
-
- params = {
- 'team_id': team_id
- }
- response = requests.post(
- upload_endpoint,
- files=files,
- headers=headers,
- params=params
- )
- if response.status_code == 200:
- try:
- resp_json = response.json()
- if resp_json.get("status") == "success":
- print(f"Successfully uploaded team configuration: {resp_json.get('name')} (team_id: {resp_json.get('team_id')})")
- uploaded_count += 1
- else:
- print(f"Upload failed for {filename}. Response: {resp_json}")
- except Exception as e:
- print(f"Error parsing response for {filename}: {str(e)}")
- else:
- print(f"Failed to upload {filename}. Status code: {response.status_code}, Response: {response.text}")
- except Exception as e:
- print(f"Error processing {filename}: {str(e)}")
- else:
- print(f"File not found: {filename}")
-print(f"Completed uploading {uploaded_count} team configurations")
\ No newline at end of file
+ try:
+ with open(file_path, 'rb') as file_data:
+ files = {
+ 'file': (filename, file_data, 'application/json')
+ }
+ headers = {
+ 'x-ms-client-principal-id': user_principal_id
+ }
+ params = {
+ 'team_id': team_id
+ }
+ response = requests.post(
+ upload_endpoint,
+ files=files,
+ headers=headers,
+ params=params
+ )
+ if response.status_code == 200:
+ try:
+ resp_json = response.json()
+ if resp_json.get("status") == "success":
+ print(f"Successfully uploaded team configuration: {resp_json.get('name')} (team_id: {resp_json.get('team_id')})")
+ uploaded_count += 1
+ else:
+ print(f"Upload failed for {filename}. Response: {resp_json}")
+ sys.exit(1)
+ except Exception as e:
+ print(f"Error parsing response for {filename}: {str(e)}")
+ sys.exit(1)
+ else:
+ print(f"Failed to upload {filename}. Status code: {response.status_code}, Response: {response.text}")
+ sys.exit(1)
+ except Exception as e:
+ print(f"Error processing {filename}: {str(e)}")
+ sys.exit(1)
+ else:
+ print(f"File not found: {filename}")
+ sys.exit(1)
+
+print(f"Completed uploading team configurations")
\ No newline at end of file
diff --git a/infra/scripts/upload_team_config.sh b/infra/scripts/upload_team_config.sh
deleted file mode 100644
index 5ca0d8d02..000000000
--- a/infra/scripts/upload_team_config.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/bin/bash
-
-# Variables
-backendUrl=$1
-directoryPath=$2
-azSubscriptionId=$3
-
-# get parameters from azd env, if not provided as arguments
-if [ -z "$directoryPath" ]; then
- directoryPath="data/agent_teams"
-fi
-
-if [ -z "$backendUrl" ]; then
- backendUrl=$(azd env get-value BACKEND_URL)
-fi
-
-if [ -z "$azSubscriptionId" ]; then
- azSubscriptionId=$(azd env get-value AZURE_SUBSCRIPTION_ID)
-fi
-
-if [ -z "$backendUrl" ] || [ -z "$directoryPath" ]; then
- echo "Error: Missing required arguments."
- echo "Usage: $0 "
- exit 1
-fi
-
-# Authenticate with Azure
-if az account show &> /dev/null; then
- echo "Already authenticated with Azure."
-else
- echo "Not authenticated with Azure. Attempting to authenticate..."
- echo "Authenticating with Azure CLI..."
- az login
-fi
-
-#check if user has selected the correct subscription
-currentSubscriptionId=$(az account show --query id -o tsv)
-currentSubscriptionName=$(az account show --query name -o tsv)
-if [ "$currentSubscriptionId" != "$azSubscriptionId" ]; then
- echo "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )."
- read -rp "Do you want to continue with this subscription?(y/n): " confirmation
- if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then
- echo "Fetching available subscriptions..."
- availableSubscriptions=$(az account list --query "[?state=='Enabled'].[name,id]" --output tsv)
- while true; do
- echo ""
- echo "Available Subscriptions:"
- echo "========================"
- echo "$availableSubscriptions" | awk '{printf "%d. %s ( %s )\n", NR, $1, $2}'
- echo "========================"
- echo ""
- read -rp "Enter the number of the subscription (1-$(echo "$availableSubscriptions" | wc -l)) to use: " subscriptionIndex
- if [[ "$subscriptionIndex" =~ ^[0-9]+$ ]] && [ "$subscriptionIndex" -ge 1 ] && [ "$subscriptionIndex" -le $(echo "$availableSubscriptions" | wc -l) ]; then
- selectedSubscription=$(echo "$availableSubscriptions" | sed -n "${subscriptionIndex}p")
- selectedSubscriptionName=$(echo "$selectedSubscription" | cut -f1)
- selectedSubscriptionId=$(echo "$selectedSubscription" | cut -f2)
-
- # Set the selected subscription
- if az account set --subscription "$selectedSubscriptionId"; then
- echo "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )"
- break
- else
- echo "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )."
- fi
- else
- echo "Invalid selection. Please try again."
- fi
- done
- else
- echo "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription "$currentSubscriptionId"
- fi
-else
- echo "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )"
- az account set --subscription "$currentSubscriptionId"
-fi
-
-userPrincipalId=$(az ad signed-in-user show --query id -o tsv)
-
-# Determine the correct Python command
-if command -v python && python --version &> /dev/null; then
- PYTHON_CMD="python"
-elif command -v python3 && python3 --version &> /dev/null; then
- PYTHON_CMD="python3"
-else
- echo "Python is not installed on this system. Or it is not added in the PATH."
- exit 1
-fi
-
-# create virtual environment
-if [ -d "infra/scripts/scriptenv" ]; then
- echo "Virtual environment already exists. Skipping creation."
-else
- echo "Creating virtual environment"
- $PYTHON_CMD -m venv infra/scripts/scriptenv
-fi
-
-# Activate the virtual environment
-if [ -f "infra/scripts/scriptenv/bin/activate" ]; then
- echo "Activating virtual environment (Linux/macOS)"
- source "infra/scripts/scriptenv/bin/activate"
-elif [ -f "infra/scripts/scriptenv/Scripts/activate" ]; then
- echo "Activating virtual environment (Windows)"
- source "infra/scripts/scriptenv/Scripts/activate"
-else
- echo "Error activating virtual environment. Requirements may be installed globally."
-fi
-
-# Install the requirements
-echo "Installing requirements"
-pip install --quiet -r infra/scripts/requirements.txt
-echo "Requirements installed"
-
-echo "Running the python script to upload team configuration"
-$PYTHON_CMD infra/scripts/upload_team_config.py "$backendUrl" "$directoryPath" "$userPrincipalId"
-if [ $? -ne 0 ]; then
- echo "Error: Team configuration upload failed."
- exit 1
-fi
-
-echo "Script executed successfully. Team configuration uploaded."
\ No newline at end of file
diff --git a/infra/scripts/validate_bicep_params.py b/infra/scripts/validate_bicep_params.py
new file mode 100644
index 000000000..1e87e6b15
--- /dev/null
+++ b/infra/scripts/validate_bicep_params.py
@@ -0,0 +1,423 @@
+"""
+Bicep Parameter Mapping Validator
+=================================
+Validates that parameter names in *.parameters.json files exactly match
+the param declarations in their corresponding Bicep templates.
+
+Checks performed:
+ 1. Whitespace โ parameter names must have no leading/trailing spaces.
+ 2. Existence โ every JSON parameter must map to a `param` in the Bicep file.
+ 3. Casing โ names must match exactly (case-sensitive).
+ 4. Orphaned โ required Bicep params (no default) missing from the JSON file.
+ 5. Env vars โ parameter values bound to environment variables must use the
+ AZURE_ENV_* naming convention, except for explicitly allowed
+ names (for example, AZURE_LOCATION, AZURE_EXISTING_AIPROJECT_RESOURCE_ID).
+
+Usage:
+ # Validate a specific pair
+ python validate_bicep_params.py --bicep main.bicep --params main.parameters.json
+
+ # Auto-discover all *.parameters.json files under infra/
+ python validate_bicep_params.py --dir infra
+
+ # CI mode โ exit code 1 on any error
+ python validate_bicep_params.py --dir infra --strict
+
+Returns exit-code 0 when no errors are found, 1 when errors are found (in --strict mode).
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import re
+import sys
+from dataclasses import dataclass, field
+from pathlib import Path
+
+# Environment variables exempt from the AZURE_ENV_ naming convention.
+_ENV_VAR_EXCEPTIONS = {"AZURE_LOCATION", "AZURE_EXISTING_AIPROJECT_RESOURCE_ID"}
+
+# ---------------------------------------------------------------------------
+# Bicep param parser
+# ---------------------------------------------------------------------------
+
+# Matches lines like: param environmentName string
+# param tags resourceInput<...>
+# param gptDeploymentCapacity int = 150
+# Ignores commented-out lines (// param ...).
+# Captures the type token and the rest of the line so we can detect defaults.
+_PARAM_RE = re.compile(
+ r"^(?!//)[ \t]*param\s+(?P[A-Za-z_]\w*)\s+(?P\S+)(?P.*)",
+ re.MULTILINE,
+)
+
+
+@dataclass
+class BicepParam:
+ name: str
+ has_default: bool
+
+
+def parse_bicep_params(bicep_path: Path) -> list[BicepParam]:
+ """Extract all `param` declarations from a Bicep file."""
+ text = bicep_path.read_text(encoding="utf-8-sig")
+ params: list[BicepParam] = []
+ for match in _PARAM_RE.finditer(text):
+ name = match.group("name")
+ param_type = match.group("type")
+ rest = match.group("rest")
+ # A param is optional if it has a default value (= ...) or is nullable (type ends with ?)
+ has_default = "=" in rest or param_type.endswith("?")
+ params.append(BicepParam(name=name, has_default=has_default))
+ return params
+
+
+# ---------------------------------------------------------------------------
+# Parameters JSON parser
+# ---------------------------------------------------------------------------
+
+
+def parse_parameters_json(json_path: Path) -> list[str]:
+ """Return the raw parameter key names (preserving whitespace) from a
+ parameters JSON file."""
+ text = json_path.read_text(encoding="utf-8-sig")
+ # azd parameter files may include ${VAR} or ${VAR=default} placeholders inside
+ # string values. These are valid JSON strings, but we sanitize them so that
+ # json.loads remains resilient to azd-specific placeholders and any unusual
+ # default formats.
+ sanitized = re.sub(r'"\$\{[^}]+\}"', '"__placeholder__"', text)
+ try:
+ data = json.loads(sanitized)
+ except json.JSONDecodeError:
+ # Fallback: extract keys with regex for resilience.
+ return _extract_keys_regex(text)
+ return list(data.get("parameters", {}).keys())
+
+
+def parse_parameters_env_vars(json_path: Path) -> dict[str, list[str]]:
+ """Return a mapping of parameter name โ list of azd env var names
+ referenced in its value (e.g. ``${AZURE_ENV_NAME}``)."""
+ text = json_path.read_text(encoding="utf-8-sig")
+ result: dict[str, list[str]] = {}
+ params = {}
+
+ # Parse the JSON to get the proper parameter structure.
+ sanitized = re.sub(r'"\$\{([^}]+)\}"', r'"__azd_\1__"', text)
+ try:
+ data = json.loads(sanitized)
+ params = data.get("parameters", {})
+ except json.JSONDecodeError:
+ # Malformed JSON cannot be reliably parsed for env-var extraction;
+ # return an empty mapping so the caller can still proceed.
+ return {}
+
+ # Walk each top-level parameter and scan its entire serialized value
+ # for ${VAR} references from the original text.
+ for param_name, param_obj in params.items():
+ # Find the raw text block for this parameter in the original file
+ # by scanning for all ${VAR} patterns in the original value section.
+ raw_value = json.dumps(param_obj)
+ # Restore original var references from the sanitized placeholders
+ for m in re.finditer(r'__azd_([^_].*?)__', raw_value):
+ var_ref = m.group(1)
+ # var_ref may contain "=default", extract just the var name
+ var_name = var_ref.split("=")[0].strip()
+ if re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', var_name):
+ result.setdefault(param_name, []).append(var_name)
+
+ return result
+
+
+def _extract_keys_regex(text: str) -> list[str]:
+ """Fallback key extraction via regex when JSON is non-standard."""
+ # Matches the key inside "parameters": { "key": ... }
+ keys: list[str] = []
+ in_params = False
+ for line in text.splitlines():
+ if '"parameters"' in line:
+ in_params = True
+ continue
+ if in_params:
+ m = re.match(r'\s*"([^"]+)"\s*:', line)
+ if m:
+ keys.append(m.group(1))
+ return keys
+
+
+# ---------------------------------------------------------------------------
+# Validation logic
+# ---------------------------------------------------------------------------
+
+@dataclass
+class ValidationIssue:
+ severity: str # "ERROR" or "WARNING"
+ param_file: str
+ bicep_file: str
+ param_name: str
+ message: str
+
+
+@dataclass
+class ValidationResult:
+ pair: str
+ issues: list[ValidationIssue] = field(default_factory=list)
+
+ @property
+ def has_errors(self) -> bool:
+ return any(i.severity == "ERROR" for i in self.issues)
+
+
+def validate_pair(
+ bicep_path: Path,
+ params_path: Path,
+) -> ValidationResult:
+ """Validate a single (bicep, parameters.json) pair."""
+ result = ValidationResult(
+ pair=f"{params_path.name} -> {bicep_path.name}"
+ )
+
+ bicep_params = parse_bicep_params(bicep_path)
+ bicep_names = {p.name for p in bicep_params}
+ bicep_names_lower = {p.name.lower(): p.name for p in bicep_params}
+ required_bicep = {p.name for p in bicep_params if not p.has_default}
+
+ json_keys = parse_parameters_json(params_path)
+
+ seen_json_keys: set[str] = set()
+
+ for raw_key in json_keys:
+ stripped = raw_key.strip()
+
+ # 1. Whitespace check
+ if raw_key != stripped:
+ result.issues.append(ValidationIssue(
+ severity="ERROR",
+ param_file=str(params_path),
+ bicep_file=str(bicep_path),
+ param_name=repr(raw_key),
+ message=(
+ f"Parameter name has leading/trailing whitespace. "
+ f"Raw key: {repr(raw_key)}, expected: {repr(stripped)}"
+ ),
+ ))
+
+ # 2. Exact match check
+ if stripped not in bicep_names:
+ # 3. Case-insensitive near-match
+ suggestion = bicep_names_lower.get(stripped.lower())
+ if suggestion:
+ result.issues.append(ValidationIssue(
+ severity="ERROR",
+ param_file=str(params_path),
+ bicep_file=str(bicep_path),
+ param_name=stripped,
+ message=(
+ f"Case mismatch: JSON has '{stripped}', "
+ f"Bicep declares '{suggestion}'."
+ ),
+ ))
+ else:
+ result.issues.append(ValidationIssue(
+ severity="ERROR",
+ param_file=str(params_path),
+ bicep_file=str(bicep_path),
+ param_name=stripped,
+ message=(
+ f"Parameter '{stripped}' exists in JSON but has no "
+ f"matching param in the Bicep template."
+ ),
+ ))
+ seen_json_keys.add(stripped)
+
+ # 4. Required Bicep params missing from JSON
+ for req in sorted(required_bicep - seen_json_keys):
+ result.issues.append(ValidationIssue(
+ severity="WARNING",
+ param_file=str(params_path),
+ bicep_file=str(bicep_path),
+ param_name=req,
+ message=(
+ f"Required Bicep param '{req}' (no default value) is not "
+ f"supplied in the parameters file."
+ ),
+ ))
+
+ # 5. Env var naming convention โ all azd vars should start with AZURE_ENV_
+ env_vars = parse_parameters_env_vars(params_path)
+ for param_name, var_names in sorted(env_vars.items()):
+ for var in var_names:
+ if not var.startswith("AZURE_ENV_") and var not in _ENV_VAR_EXCEPTIONS:
+ result.issues.append(ValidationIssue(
+ severity="WARNING",
+ param_file=str(params_path),
+ bicep_file=str(bicep_path),
+ param_name=param_name,
+ message=(
+ f"Env var '${{{var}}}' does not follow the "
+ f"AZURE_ENV_ naming convention."
+ ),
+ ))
+
+ return result
+
+
+# ---------------------------------------------------------------------------
+# Discovery โ find (bicep, params) pairs automatically
+# ---------------------------------------------------------------------------
+
+def discover_pairs(infra_dir: Path) -> list[tuple[Path, Path]]:
+ """For each *.parameters.json, find the matching Bicep file.
+
+ Naming convention: a file like ``main.waf.parameters.json`` is a
+ variant of ``main.parameters.json`` โ the user copies its contents
+ into ``main.parameters.json`` before running ``azd up``. Both
+ files should therefore be validated against ``main.bicep``.
+
+ Resolution order:
+ 1. Exact stem match (e.g. ``foo.parameters.json`` โ ``foo.bicep``).
+ 2. Base-stem match (e.g. ``main.waf.parameters.json`` โ ``main.bicep``).
+ """
+ pairs: list[tuple[Path, Path]] = []
+ for pf in sorted(infra_dir.rglob("*.parameters.json")):
+ stem = pf.name.replace(".parameters.json", "")
+ bicep_candidate = pf.parent / f"{stem}.bicep"
+ if bicep_candidate.exists():
+ pairs.append((bicep_candidate, pf))
+ else:
+ # Try the base stem (first segment before the first dot).
+ base_stem = stem.split(".")[0]
+ base_candidate = pf.parent / f"{base_stem}.bicep"
+ if base_candidate.exists():
+ pairs.append((base_candidate, pf))
+ else:
+ print(f" [SKIP] No matching Bicep file for {pf.name}")
+ return pairs
+
+
+# ---------------------------------------------------------------------------
+# Reporting
+# ---------------------------------------------------------------------------
+
+_COLORS = {
+ "ERROR": "\033[91m", # red
+ "WARNING": "\033[93m", # yellow
+ "OK": "\033[92m", # green
+ "RESET": "\033[0m",
+}
+
+
+def print_report(results: list[ValidationResult], *, use_color: bool = True) -> None:
+ c = _COLORS if use_color else {k: "" for k in _COLORS}
+ total_errors = 0
+ total_warnings = 0
+
+ for r in results:
+ errors = [i for i in r.issues if i.severity == "ERROR"]
+ warnings = [i for i in r.issues if i.severity == "WARNING"]
+ total_errors += len(errors)
+ total_warnings += len(warnings)
+
+ if not r.issues:
+ print(f"\n{c['OK']}[PASS]{c['RESET']} {r.pair}")
+ elif errors:
+ print(f"\n{c['ERROR']}[FAIL]{c['RESET']} {r.pair}")
+ else:
+ print(f"\n{c['WARNING']}[WARN]{c['RESET']} {r.pair}")
+
+ for issue in r.issues:
+ tag = (
+ f"{c['ERROR']}ERROR{c['RESET']}"
+ if issue.severity == "ERROR"
+ else f"{c['WARNING']}WARN {c['RESET']}"
+ )
+ print(f" {tag} {issue.param_name}: {issue.message}")
+
+ print(f"\n{'='*60}")
+ print(f"Total: {total_errors} error(s), {total_warnings} warning(s)")
+ if total_errors == 0:
+ print(f"{c['OK']}All parameter mappings are valid.{c['RESET']}")
+ else:
+ print(f"{c['ERROR']}Parameter mapping issues detected!{c['RESET']}")
+
+
+# ---------------------------------------------------------------------------
+# CLI
+# ---------------------------------------------------------------------------
+
+def main() -> int:
+ parser = argparse.ArgumentParser(
+ description="Validate Bicep โ parameters.json parameter mappings.",
+ )
+ parser.add_argument(
+ "--bicep",
+ type=Path,
+ help="Path to a specific Bicep template.",
+ )
+ parser.add_argument(
+ "--params",
+ type=Path,
+ help="Path to a specific parameters JSON file.",
+ )
+ parser.add_argument(
+ "--dir",
+ type=Path,
+ help="Directory to scan for *.parameters.json files (auto-discovers pairs).",
+ )
+ parser.add_argument(
+ "--strict",
+ action="store_true",
+ help="Exit with code 1 if any errors are found.",
+ )
+ parser.add_argument(
+ "--no-color",
+ action="store_true",
+ help="Disable colored output (useful for CI logs).",
+ )
+ parser.add_argument(
+ "--json-output",
+ type=Path,
+ help="Write results as JSON to the given file path.",
+ )
+ args = parser.parse_args()
+
+ results: list[ValidationResult] = []
+
+ if args.bicep and args.params:
+ results.append(validate_pair(args.bicep, args.params))
+ elif args.dir:
+ pairs = discover_pairs(args.dir)
+ if not pairs:
+ print(f"No (bicep, parameters.json) pairs found under {args.dir}")
+ return 0
+ for bicep_path, params_path in pairs:
+ results.append(validate_pair(bicep_path, params_path))
+ else:
+ parser.error("Provide either --bicep/--params or --dir.")
+
+ print_report(results, use_color=not args.no_color)
+
+ # Optional JSON output for CI artifact consumption
+ if args.json_output:
+ json_data = []
+ for r in results:
+ for issue in r.issues:
+ json_data.append({
+ "severity": issue.severity,
+ "paramFile": issue.param_file,
+ "bicepFile": issue.bicep_file,
+ "paramName": issue.param_name,
+ "message": issue.message,
+ })
+ args.json_output.parent.mkdir(parents=True, exist_ok=True)
+ args.json_output.write_text(
+ json.dumps(json_data, indent=2), encoding="utf-8"
+ )
+ print(f"\nJSON report written to {args.json_output}")
+
+ has_errors = any(r.has_errors for r in results)
+ return 1 if args.strict and has_errors else 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/infra/vscode_web/.env b/infra/vscode_web/.env
new file mode 100644
index 000000000..141104744
--- /dev/null
+++ b/infra/vscode_web/.env
@@ -0,0 +1,7 @@
+AZURE_EXISTING_AGENT_ID="<%= agentId %>"
+AZURE_ENV_NAME="<%= playgroundName %>"
+# AZURE_LOCATION="<%= location %>"
+AZURE_SUBSCRIPTION_ID="<%= subscriptionId %>"
+AZURE_EXISTING_AIPROJECT_ENDPOINT="<%= endpoint %>"
+AZURE_EXISTING_AIPROJECT_RESOURCE_ID="<%= projectResourceId %>"
+AZD_ALLOW_NON_EMPTY_FOLDER=true
diff --git a/infra/vscode_web/.gitignore b/infra/vscode_web/.gitignore
new file mode 100644
index 000000000..23de01ef5
--- /dev/null
+++ b/infra/vscode_web/.gitignore
@@ -0,0 +1,85 @@
+# ========== .NET ========== #
+## Build results
+bin/
+obj/
+[Bb]uild/
+[Ll]ogs/
+*.log
+## User-specific files
+*.user
+*.suo
+*.userosscache
+*.sln.docstates
+*.vsp
+*.vspx
+*.vspscc
+## Rider / VS Code / Visual Studio
+.idea/
+.vscode/
+.vs/
+## NuGet packages
+*.nupkg
+packages/
+*.snupkg
+project.lock.json
+project.assets.json
+## Dotnet tools
+.tools/
+# ========== Java ========== #
+## Compiled class files
+*.class
+## Logs
+*.log
+## Maven
+target/
+## Gradle
+.gradle/
+build/
+## Eclipse
+.project
+.classpath
+.settings/
+.loadpath
+## IntelliJ IDEA
+*.iml
+*.ipr
+*.iws
+out/
+.idea/
+# ========== Python ========== #
+## Byte-compiled / cache
+__pycache__/
+*.py[cod]
+*$py.class
+## Virtual environment
+env/
+venv/
+ENV/
+.venv/
+.env*
+## PyInstaller
+*.spec
+dist/
+build/
+## Jupyter Notebook
+.ipynb_checkpoints/
+## Misc
+*.log
+*.pot
+*.pyc
+.DS_Store
+*.sqlite3
+# ========== General ========== #
+## OS generated
+Thumbs.db
+ehthumbs.db
+Desktop.ini
+.DS_Store
+*.swp
+*.swo
+*.bak
+*.tmp
+*.old
+## Node (just in case mixed project)
+node_modules/
+# End
\ No newline at end of file
diff --git a/infra/vscode_web/LICENSE b/infra/vscode_web/LICENSE
new file mode 100644
index 000000000..22aed37e6
--- /dev/null
+++ b/infra/vscode_web/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) Microsoft Corporation.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/infra/vscode_web/README-noazd.md b/infra/vscode_web/README-noazd.md
new file mode 100644
index 000000000..1436b6150
--- /dev/null
+++ b/infra/vscode_web/README-noazd.md
@@ -0,0 +1,2 @@
+# VS Code for the Web - Azure AI Foundry Templates
+
diff --git a/infra/vscode_web/README.md b/infra/vscode_web/README.md
new file mode 100644
index 000000000..6ce5aedfa
--- /dev/null
+++ b/infra/vscode_web/README.md
@@ -0,0 +1,43 @@
+# VS Code for the Web - Azure AI Foundry Templates
+
+We've generated a simple development environment for you to deploy the templates.
+
+The Azure AI Foundry extension provides tools to help you build, test, and deploy AI models and AI Applications directly from VS Code. It offers simplified operations for interacting with your models, agents, and threads without leaving your development environment. Click on the Azure AI Foundry Icon on the left to see more.
+
+Follow the instructions below to get started!
+
+You should see a terminal opened with the template code already cloned.
+
+## Deploy the template
+
+You can provision and deploy this template using:
+
+```bash
+azd up
+```
+
+Follow any instructions from the deployment script and launch the application.
+
+
+If you need to delete the deployment and stop incurring any charges, run:
+
+```bash
+azd down
+```
+
+## Continuing on your local desktop
+
+You can keep working locally on VS Code Desktop by clicking "Continue On Desktop..." at the bottom left of this screen. Be sure to take the .env file with you using these steps:
+
+- Right-click the .env file
+- Select "Download"
+- Move the file from your Downloads folder to the local git repo directory
+- For Windows, you will need to rename the file back to .env using right-click "Rename..."
+
+## More examples
+
+Check out [Azure AI Projects client library for Python](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/README.md) for more information on using this SDK.
+
+## Troubleshooting
+
+- If you are instantiating your client via endpoint on an Azure AI Foundry project, ensure the endpoint is set in the `.env` as https://{your-foundry-resource-name}.services.ai.azure.com/api/projects/{your-foundry-project-name}`
\ No newline at end of file
diff --git a/infra/vscode_web/codeSample.py b/infra/vscode_web/codeSample.py
new file mode 100644
index 000000000..37224009c
--- /dev/null
+++ b/infra/vscode_web/codeSample.py
@@ -0,0 +1,25 @@
+from azure.ai.projects import AIProjectClient
+from azure.identity import DefaultAzureCredential
+
+project_client = AIProjectClient.from_connection_string(
+ credential=DefaultAzureCredential(),
+ conn_str="<%= connectionString %>")
+
+agent = project_client.agents.get_agent("<%= agentId %>")
+
+thread = project_client.agents.create_thread()
+print(f"Created thread, ID: {thread.id}")
+
+message = project_client.agents.create_message(
+ thread_id=thread.id,
+ role="user",
+ content="<%= userMessage %>"
+)
+
+run = project_client.agents.create_and_process_run(
+ thread_id=thread.id,
+ agent_id=agent.id)
+messages = project_client.agents.list_messages(thread_id=thread.id)
+
+for text_message in messages.text_messages:
+ print(text_message.as_dict())
diff --git a/infra/vscode_web/endpoint-requirements.txt b/infra/vscode_web/endpoint-requirements.txt
new file mode 100644
index 000000000..18d6803e8
--- /dev/null
+++ b/infra/vscode_web/endpoint-requirements.txt
@@ -0,0 +1,3 @@
+azure-ai-projects==1.0.0b12
+azure-identity==1.20.0
+ansible-core~=2.17.0
\ No newline at end of file
diff --git a/infra/vscode_web/endpointCodeSample.py b/infra/vscode_web/endpointCodeSample.py
new file mode 100644
index 000000000..da4237474
--- /dev/null
+++ b/infra/vscode_web/endpointCodeSample.py
@@ -0,0 +1,31 @@
+from azure.ai.agents.models import ListSortOrder
+from azure.ai.projects import AIProjectClient
+from azure.identity import DefaultAzureCredential
+
+project = AIProjectClient(
+ credential=DefaultAzureCredential(),
+ endpoint="<%= endpoint %>")
+
+agent = project.agents.get_agent("<%= agentId %>")
+
+thread = project.agents.threads.create()
+print(f"Created thread, ID: {thread.id}")
+
+message = project.agents.messages.create(
+ thread_id=thread.id,
+ role="user",
+ content="<%= userMessage %>"
+)
+
+run = project.agents.runs.create_and_process(
+ thread_id=thread.id,
+ agent_id=agent.id)
+
+if run.status == "failed":
+ print(f"Run failed: {run.last_error}")
+else:
+ messages = project.agents.messages.list(thread_id=thread.id, order=ListSortOrder.ASCENDING)
+
+ for message in messages:
+ if message.text_messages:
+ print(f"{message.role}: {message.text_messages[-1].text.value}")
diff --git a/infra/vscode_web/index.json b/infra/vscode_web/index.json
new file mode 100644
index 000000000..55157c9da
--- /dev/null
+++ b/infra/vscode_web/index.json
@@ -0,0 +1,72 @@
+{
+ "ai-projects-sdk": {
+ "python": {
+ "default-azure-auth": {
+ "connectionString": [
+ {
+ "name": "run_agent.py",
+ "type": "code",
+ "path": "/codeSample.py"
+ },
+ {
+ "name": "INSTRUCTIONS.md",
+ "type": "readme",
+ "path": "/README-noazd.md"
+ },
+ {
+ "name": "requirements.txt",
+ "type": "dependencies",
+ "path": "/requirements.txt"
+ },
+ {
+ "name": ".env",
+ "type": "env",
+ "path": "/.env"
+ },
+ {
+ "name": "install.sh",
+ "type": "install",
+ "path": "/install.sh"
+ },
+ {
+ "name": ".gitignore",
+ "type": "code",
+ "path": "/.gitignore"
+ }
+ ],
+ "endpoint": [
+ {
+ "name": "run_agent.py",
+ "type": "code",
+ "path": "/endpointCodeSample.py"
+ },
+ {
+ "name": "INSTRUCTIONS.md",
+ "type": "readme",
+ "path": "/README.md"
+ },
+ {
+ "name": "requirements.txt",
+ "type": "dependencies",
+ "path": "/endpoint-requirements.txt"
+ },
+ {
+ "name": ".env",
+ "type": "env",
+ "path": "/.env"
+ },
+ {
+ "name": "install.sh",
+ "type": "install",
+ "path": "/install.sh"
+ },
+ {
+ "name": ".gitignore",
+ "type": "code",
+ "path": "/.gitignore"
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/infra/vscode_web/install.sh b/infra/vscode_web/install.sh
new file mode 100644
index 000000000..2c6920d57
--- /dev/null
+++ b/infra/vscode_web/install.sh
@@ -0,0 +1,3 @@
+pip install -r requirements.txt --user -q
+
+azd init -t microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator
\ No newline at end of file
diff --git a/infra/vscode_web/requirements.txt b/infra/vscode_web/requirements.txt
new file mode 100644
index 000000000..18d6803e8
--- /dev/null
+++ b/infra/vscode_web/requirements.txt
@@ -0,0 +1,3 @@
+azure-ai-projects==1.0.0b12
+azure-identity==1.20.0
+ansible-core~=2.17.0
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..61d91fc72
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "Multi-Agent-Custom-Automation-Engine-Solution-Accelerator",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/pytest.ini b/pytest.ini
index 987d4460f..3608e5bda 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,2 +1,3 @@
[pytest]
addopts = -p pytest_asyncio
+asyncio_mode = strict
diff --git a/src/frontend/.dockerignore b/src/App/.dockerignore
similarity index 100%
rename from src/frontend/.dockerignore
rename to src/App/.dockerignore
diff --git a/src/frontend/.env.sample b/src/App/.env.sample
similarity index 100%
rename from src/frontend/.env.sample
rename to src/App/.env.sample
diff --git a/src/frontend/.eslintrc.js b/src/App/.eslintrc.js
similarity index 100%
rename from src/frontend/.eslintrc.js
rename to src/App/.eslintrc.js
diff --git a/src/frontend/.gitignore b/src/App/.gitignore
similarity index 100%
rename from src/frontend/.gitignore
rename to src/App/.gitignore
diff --git a/src/frontend/.python-version b/src/App/.python-version
similarity index 100%
rename from src/frontend/.python-version
rename to src/App/.python-version
diff --git a/src/frontend/Dockerfile b/src/App/Dockerfile
similarity index 100%
rename from src/frontend/Dockerfile
rename to src/App/Dockerfile
diff --git a/src/frontend/README.md b/src/App/README.md
similarity index 100%
rename from src/frontend/README.md
rename to src/App/README.md
diff --git a/src/frontend/frontend_server.py b/src/App/frontend_server.py
similarity index 81%
rename from src/frontend/frontend_server.py
rename to src/App/frontend_server.py
index 47b181aaf..d95c4535b 100644
--- a/src/frontend/frontend_server.py
+++ b/src/App/frontend_server.py
@@ -1,11 +1,10 @@
-import html
import os
import uvicorn
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
-from fastapi.responses import FileResponse, HTMLResponse
+from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
# Load environment variables from .env file
@@ -37,8 +36,8 @@ async def serve_index():
@app.get("/config")
async def get_config():
- backend_url = html.escape(os.getenv("BACKEND_API_URL", "http://localhost:8000"))
- auth_enabled = html.escape(os.getenv("AUTH_ENABLED", "false"))
+ backend_url = os.getenv("BACKEND_API_URL", "http://localhost:8000")
+ auth_enabled = os.getenv("AUTH_ENABLED", "false")
backend_url = backend_url + "/api"
config = {
@@ -53,11 +52,17 @@ async def serve_app(full_path: str):
# Remediation: normalize and check containment before serving
file_path = os.path.normpath(os.path.join(BUILD_DIR, full_path))
# Block traversal and dotfiles
- if not file_path.startswith(BUILD_DIR) or ".." in full_path or "/." in full_path or "\\." in full_path:
+ if (
+ not file_path.startswith(BUILD_DIR)
+ or ".." in full_path
+ or "/." in full_path
+ or "\\." in full_path
+ ):
return FileResponse(INDEX_HTML)
if os.path.isfile(file_path):
return FileResponse(file_path)
return FileResponse(INDEX_HTML)
+
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=3000, access_log=False, log_level="info")
diff --git a/src/frontend/index.html b/src/App/index.html
similarity index 100%
rename from src/frontend/index.html
rename to src/App/index.html
diff --git a/src/frontend/migration-commands.txt b/src/App/migration-commands.txt
similarity index 100%
rename from src/frontend/migration-commands.txt
rename to src/App/migration-commands.txt
diff --git a/src/frontend/package-lock.json b/src/App/package-lock.json
similarity index 50%
rename from src/frontend/package-lock.json
rename to src/App/package-lock.json
index 3e17847f7..d1483bcb2 100644
--- a/src/frontend/package-lock.json
+++ b/src/App/package-lock.json
@@ -11,6 +11,7 @@
"@fluentui/merge-styles": "^8.6.14",
"@fluentui/react-components": "^9.64.0",
"@fluentui/react-icons": "^2.0.300",
+ "@reduxjs/toolkit": "^2.11.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
@@ -19,11 +20,11 @@
"@types/node": "^16.18.126",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
- "axios": "^1.11.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^10.1.0",
- "react-router-dom": "^7.6.0",
+ "react-redux": "^9.2.0",
+ "react-router-dom": "^7.12.0",
"rehype-prism": "^2.3.3",
"remark-gfm": "^4.0.1",
"web-vitals": "^2.1.4"
@@ -36,22 +37,20 @@
"@vitest/ui": "^3.2.4",
"eslint": "^8.57.1",
"eslint-plugin-react": "^7.37.5",
+ "flatted": "^3.4.2",
"jsdom": "^26.1.0",
+ "rollup": "^4.59.0",
"typescript": "^5.8.3",
- "vite": "^7.1.2",
+ "vite": "7.3.2",
"vitest": "^3.2.4"
}
},
"node_modules/@adobe/css-tools": {
"version": "4.4.4",
- "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
- "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==",
"license": "MIT"
},
"node_modules/@asamuzakjp/css-color": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
- "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -64,18 +63,14 @@
},
"node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
"version": "10.4.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "version": "7.29.0",
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -84,9 +79,7 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
- "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "version": "7.29.0",
"dev": true,
"license": "MIT",
"engines": {
@@ -94,21 +87,19 @@
}
},
"node_modules/@babel/core": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
- "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
+ "version": "7.29.0",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.4",
- "@babel/parser": "^7.28.4",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.4",
- "@babel/types": "^7.28.4",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
@@ -126,8 +117,6 @@
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -135,14 +124,12 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
- "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "version": "7.29.1",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -152,13 +139,11 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "version": "7.28.6",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.27.2",
+ "@babel/compat-data": "^7.28.6",
"@babel/helper-validator-option": "^7.27.1",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
@@ -170,8 +155,6 @@
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
"version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -180,8 +163,6 @@
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -189,29 +170,25 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "version": "7.28.6",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
- "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "version": "7.28.6",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.28.3"
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -221,9 +198,7 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "version": "7.28.6",
"dev": true,
"license": "MIT",
"engines": {
@@ -232,8 +207,6 @@
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -241,9 +214,7 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "version": "7.28.5",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -251,8 +222,6 @@
},
"node_modules/@babel/helper-validator-option": {
"version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -260,27 +229,23 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
- "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "version": "7.29.2",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4"
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
- "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+ "version": "7.29.2",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.4"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -291,8 +256,6 @@
},
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
- "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -307,8 +270,6 @@
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
"version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
- "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -322,42 +283,36 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
- "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "version": "7.29.2",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "version": "7.28.6",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
- "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
+ "version": "7.29.0",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.4",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
"debug": "^4.3.1"
},
"engines": {
@@ -365,14 +320,12 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
- "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+ "version": "7.29.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
@@ -380,8 +333,6 @@
},
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
- "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
"dev": true,
"funding": [
{
@@ -400,8 +351,6 @@
},
"node_modules/@csstools/css-calc": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
- "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
"dev": true,
"funding": [
{
@@ -424,8 +373,6 @@
},
"node_modules/@csstools/css-color-parser": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
- "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
"dev": true,
"funding": [
{
@@ -452,8 +399,6 @@
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
- "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
"dev": true,
"funding": [
{
@@ -475,8 +420,6 @@
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
- "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
"dev": true,
"funding": [
{
@@ -495,8 +438,6 @@
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
- "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
- "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
"license": "MIT",
"engines": {
"node": ">=10"
@@ -504,14 +445,12 @@
},
"node_modules/@emotion/hash": {
"version": "0.9.2",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
- "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
- "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
"cpu": [
"ppc64"
],
@@ -526,9 +465,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
- "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
"cpu": [
"arm"
],
@@ -543,9 +482,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
- "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
"cpu": [
"arm64"
],
@@ -560,9 +499,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
- "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
"cpu": [
"x64"
],
@@ -577,9 +516,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
- "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
"cpu": [
"arm64"
],
@@ -594,9 +533,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
- "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
"cpu": [
"x64"
],
@@ -611,9 +550,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
- "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
"cpu": [
"arm64"
],
@@ -628,9 +567,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
- "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
"cpu": [
"x64"
],
@@ -645,9 +584,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
- "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
"cpu": [
"arm"
],
@@ -662,9 +601,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
- "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
"cpu": [
"arm64"
],
@@ -679,9 +618,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
- "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
"cpu": [
"ia32"
],
@@ -696,9 +635,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
- "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
"cpu": [
"loong64"
],
@@ -713,9 +652,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
- "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
"cpu": [
"mips64el"
],
@@ -730,9 +669,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
- "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
"cpu": [
"ppc64"
],
@@ -747,9 +686,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
- "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
"cpu": [
"riscv64"
],
@@ -764,9 +703,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
- "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
"cpu": [
"s390x"
],
@@ -781,9 +720,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
- "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
"cpu": [
"x64"
],
@@ -798,9 +737,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
"cpu": [
"arm64"
],
@@ -815,9 +754,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
- "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
"cpu": [
"x64"
],
@@ -832,9 +771,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
"cpu": [
"arm64"
],
@@ -849,9 +788,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
- "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
"cpu": [
"x64"
],
@@ -866,9 +805,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
- "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
"cpu": [
"arm64"
],
@@ -883,9 +822,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
- "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
"cpu": [
"x64"
],
@@ -900,9 +839,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
- "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
"cpu": [
"arm64"
],
@@ -917,9 +856,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
- "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
"cpu": [
"ia32"
],
@@ -934,9 +873,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
- "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
"cpu": [
"x64"
],
@@ -951,9 +890,7 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
- "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "version": "4.9.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -970,9 +907,7 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "version": "4.12.2",
"dev": true,
"license": "MIT",
"engines": {
@@ -981,8 +916,6 @@
},
"node_modules/@eslint/eslintrc": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1005,8 +938,6 @@
},
"node_modules/@eslint/js": {
"version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
- "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1014,43 +945,33 @@
}
},
"node_modules/@floating-ui/core": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
- "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "version": "1.7.5",
"license": "MIT",
"dependencies": {
- "@floating-ui/utils": "^0.2.10"
+ "@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/devtools": {
"version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/devtools/-/devtools-0.2.3.tgz",
- "integrity": "sha512-ZTcxTvgo9CRlP7vJV62yCxdqmahHTGpSTi5QaTDgGoyQq0OyjaVZhUhXv/qdkQFOI3Sxlfmz0XGG4HaZMsDf8Q==",
"license": "MIT",
"peerDependencies": {
"@floating-ui/dom": "^1.0.0"
}
},
"node_modules/@floating-ui/dom": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
- "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "version": "1.7.6",
"license": "MIT",
"dependencies": {
- "@floating-ui/core": "^1.7.3",
- "@floating-ui/utils": "^0.2.10"
+ "@floating-ui/core": "^1.7.5",
+ "@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/utils": {
- "version": "0.2.10",
- "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
- "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "version": "0.2.11",
"license": "MIT"
},
"node_modules/@fluentui/keyboard-keys": {
"version": "9.0.8",
- "resolved": "https://registry.npmjs.org/@fluentui/keyboard-keys/-/keyboard-keys-9.0.8.tgz",
- "integrity": "sha512-iUSJUUHAyTosnXK8O2Ilbfxma+ZyZPMua5vB028Ys96z80v+LFwntoehlFsdH3rMuPsA8GaC1RE7LMezwPBPdw==",
"license": "MIT",
"dependencies": {
"@swc/helpers": "^0.5.1"
@@ -1058,8 +979,6 @@
},
"node_modules/@fluentui/merge-styles": {
"version": "8.6.14",
- "resolved": "https://registry.npmjs.org/@fluentui/merge-styles/-/merge-styles-8.6.14.tgz",
- "integrity": "sha512-vghuHFAfQgS9WLIIs4kgDOCh/DHd5vGIddP4/bzposhlAVLZR6wUBqldm9AuCdY88r5LyCRMavVJLV+Up3xdvA==",
"license": "MIT",
"dependencies": {
"@fluentui/set-version": "^8.2.24",
@@ -1067,1551 +986,1423 @@
}
},
"node_modules/@fluentui/priority-overflow": {
- "version": "9.1.15",
- "resolved": "https://registry.npmjs.org/@fluentui/priority-overflow/-/priority-overflow-9.1.15.tgz",
- "integrity": "sha512-/3jPBBq64hRdA416grVj+ZeMBUIaKZk2S5HiRg7CKCAV1JuyF84Do0rQI6ns8Vb9XOGuc4kurMcL/UEftoEVrg==",
+ "version": "9.3.0",
"license": "MIT",
"dependencies": {
"@swc/helpers": "^0.5.1"
}
},
"node_modules/@fluentui/react-accordion": {
- "version": "9.8.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-accordion/-/react-accordion-9.8.5.tgz",
- "integrity": "sha512-e3RNtrzTgTRSwueOaxjQimG3u8QQUa8EiTIpRThadedleVtS0KWfuvSv2/EKUL85I6toaTthOFFuJRpP6C9Frw==",
+ "version": "9.10.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-motion-components-preview": "^0.9.0",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-alert": {
- "version": "9.0.0-beta.124",
- "resolved": "https://registry.npmjs.org/@fluentui/react-alert/-/react-alert-9.0.0-beta.124.tgz",
- "integrity": "sha512-yFBo3B5H9hnoaXxlkuz8wRz04DEyQ+ElYA/p5p+Vojf19Zuta8DmFZZ6JtWdtxcdnnQ4LvAfC5OYYlzdReozPA==",
+ "version": "9.0.0-beta.138",
"license": "MIT",
"dependencies": {
- "@fluentui/react-avatar": "^9.6.29",
- "@fluentui/react-button": "^9.3.83",
+ "@fluentui/react-avatar": "^9.11.0",
+ "@fluentui/react-button": "^9.9.0",
"@fluentui/react-icons": "^2.0.239",
- "@fluentui/react-jsx-runtime": "^9.0.39",
- "@fluentui/react-tabster": "^9.21.5",
- "@fluentui/react-theme": "^9.1.19",
- "@fluentui/react-utilities": "^9.18.10",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-aria": {
- "version": "9.16.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-aria/-/react-aria-9.16.4.tgz",
- "integrity": "sha512-ent+vc93+6EAeg26tnZMoRp8lIJtfFMbKFAa0WvZGbN5jU24NQUniJCdXcsfrmVCQ2hHophQDvUSwGhPkABURw==",
+ "version": "9.17.10",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-utilities": "^9.24.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-utilities": "^9.26.2",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-avatar": {
- "version": "9.9.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-avatar/-/react-avatar-9.9.5.tgz",
- "integrity": "sha512-xl1oewoY7dtNCyEuhghJCzHF1RVARZdtVsuleMvI9TZuyjoKuXyOzaLSyFhh1lXGkcrSsS3JtrVrTVFyR2u/wg==",
+ "version": "9.11.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-badge": "^9.4.4",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-badge": "^9.5.1",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-popover": "^9.12.5",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-tooltip": "^9.8.4",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-popover": "^9.14.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-tooltip": "^9.10.0",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-badge": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-badge/-/react-badge-9.4.4.tgz",
- "integrity": "sha512-XfAwIweS9ypwkNsWfEApM6xLAqAJjgC4Vb31owRqUBGu+IKlKDLqhNKQPyTLVb8Ql+okiEFu7tZellCRr5K1Uw==",
+ "version": "9.5.1",
"license": "MIT",
"dependencies": {
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-breadcrumb": {
- "version": "9.3.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-breadcrumb/-/react-breadcrumb-9.3.5.tgz",
- "integrity": "sha512-AkBMEo1L81wH5UYTQs6QqOFiAbAF9xrA6V7CDSfzOO0yBAlQH5N4DD6b+Q8dEDBWPfUmy15VzYVmhQosm4Tztg==",
+ "version": "9.4.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-button": "^9.6.5",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-button": "^9.9.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-link": "^9.6.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-link": "^9.8.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-button": {
- "version": "9.6.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-button/-/react-button-9.6.5.tgz",
- "integrity": "sha512-UMhGNn82rhz4o9dAVVG/4OUI7XjZlUW4F2u8BkSh0RAUD+d3wQn4EFYSF7/VbLvdq+dgLIaCTUMkd1UerDRvYw==",
+ "version": "9.9.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
+ "@fluentui/react-aria": "^9.17.10",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-card": {
- "version": "9.4.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-card/-/react-card-9.4.5.tgz",
- "integrity": "sha512-MFsbbT38AzjvAdvFlPGetPV01FJTlPf3cC/UiKmR4nhZg2ss2H4+jh0p4Y/xHSCUUe5Q5nMtVX0+xSUrEt+Lig==",
+ "version": "9.6.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-text": "^9.6.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-text": "^9.6.15",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-carousel": {
- "version": "9.8.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-carousel/-/react-carousel-9.8.5.tgz",
- "integrity": "sha512-mSgUvznEzBGhJ3PRX8BQGILbD/C0UiKul0Ry79h3y/0A8TGm8wVFDzXOH0QQsugOio4JpUamm/fDApHodsMVmw==",
+ "version": "9.9.6",
"license": "MIT",
"dependencies": {
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-button": "^9.6.5",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-button": "^9.9.0",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-tooltip": "^9.8.4",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-tooltip": "^9.10.0",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1",
"embla-carousel": "^8.5.1",
"embla-carousel-autoplay": "^8.5.1",
"embla-carousel-fade": "^8.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-checkbox": {
- "version": "9.5.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-checkbox/-/react-checkbox-9.5.4.tgz",
- "integrity": "sha512-1OcjlGAOhtv67aUcHHXCFFO2Phmps30NcagQX1PhDjQNWCQa8k3de6obpgTNfLvD6EA8K0Yz+x4BkpwK11DxGQ==",
+ "version": "9.6.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-color-picker": {
- "version": "9.2.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-color-picker/-/react-color-picker-9.2.4.tgz",
- "integrity": "sha512-LjjwfUvD0TyWTJnAIZgpgDwLj3HtBGcW4ZlM0AllJN8q3RnxnEA5ygFrhB2bFjOc6a4ijCavKEU5ZfdtmS+Kpg==",
+ "version": "9.2.15",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^3.3.4",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-combobox": {
- "version": "9.16.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-combobox/-/react-combobox-9.16.5.tgz",
- "integrity": "sha512-hgBru9DW1XIysbfk7RsnfhwoxQ8JpaAFoPZF16sAtkM2W+WpBYWcHHnYHbntCos1TB2yDKCdOfkQDaHwgOUeQw==",
+ "version": "9.17.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-positioning": "^9.20.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-positioning": "^9.22.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-components": {
- "version": "9.69.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-components/-/react-components-9.69.0.tgz",
- "integrity": "sha512-iw6gZVdAMPgPLbAwwAcA+2wRfeHdV27tRMPfrNYnFlXMAYfcXQvWjxeD8XTL5j2PYfOhRJjnWvjL0srJjjMcfA==",
- "license": "MIT",
- "dependencies": {
- "@fluentui/react-accordion": "^9.8.5",
- "@fluentui/react-alert": "9.0.0-beta.124",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-avatar": "^9.9.5",
- "@fluentui/react-badge": "^9.4.4",
- "@fluentui/react-breadcrumb": "^9.3.5",
- "@fluentui/react-button": "^9.6.5",
- "@fluentui/react-card": "^9.4.5",
- "@fluentui/react-carousel": "^9.8.5",
- "@fluentui/react-checkbox": "^9.5.4",
- "@fluentui/react-color-picker": "^9.2.4",
- "@fluentui/react-combobox": "^9.16.5",
- "@fluentui/react-dialog": "^9.15.0",
- "@fluentui/react-divider": "^9.4.4",
- "@fluentui/react-drawer": "^9.10.0",
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-image": "^9.3.4",
- "@fluentui/react-infobutton": "9.0.0-beta.102",
- "@fluentui/react-infolabel": "^9.4.5",
- "@fluentui/react-input": "^9.7.4",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-link": "^9.6.4",
- "@fluentui/react-list": "^9.5.0",
- "@fluentui/react-menu": "^9.19.5",
- "@fluentui/react-message-bar": "^9.6.5",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-nav": "^9.3.5",
- "@fluentui/react-overflow": "^9.5.5",
- "@fluentui/react-persona": "^9.5.5",
- "@fluentui/react-popover": "^9.12.5",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-positioning": "^9.20.4",
- "@fluentui/react-progress": "^9.4.4",
- "@fluentui/react-provider": "^9.22.4",
- "@fluentui/react-radio": "^9.5.4",
- "@fluentui/react-rating": "^9.3.4",
- "@fluentui/react-search": "^9.3.4",
- "@fluentui/react-select": "^9.4.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-skeleton": "^9.4.4",
- "@fluentui/react-slider": "^9.5.4",
- "@fluentui/react-spinbutton": "^9.5.4",
- "@fluentui/react-spinner": "^9.7.4",
- "@fluentui/react-swatch-picker": "^9.4.4",
- "@fluentui/react-switch": "^9.4.4",
- "@fluentui/react-table": "^9.18.5",
- "@fluentui/react-tabs": "^9.10.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-tag-picker": "^9.7.5",
- "@fluentui/react-tags": "^9.7.5",
- "@fluentui/react-teaching-popover": "^9.6.5",
- "@fluentui/react-text": "^9.6.4",
- "@fluentui/react-textarea": "^9.6.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-toast": "^9.7.0",
- "@fluentui/react-toolbar": "^9.6.5",
- "@fluentui/react-tooltip": "^9.8.4",
- "@fluentui/react-tree": "^9.13.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@fluentui/react-virtualizer": "9.0.0-alpha.102",
- "@griffel/react": "^1.5.22",
+ "version": "9.73.7",
+ "license": "MIT",
+ "dependencies": {
+ "@fluentui/react-accordion": "^9.10.0",
+ "@fluentui/react-alert": "9.0.0-beta.138",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-avatar": "^9.11.0",
+ "@fluentui/react-badge": "^9.5.1",
+ "@fluentui/react-breadcrumb": "^9.4.0",
+ "@fluentui/react-button": "^9.9.0",
+ "@fluentui/react-card": "^9.6.0",
+ "@fluentui/react-carousel": "^9.9.6",
+ "@fluentui/react-checkbox": "^9.6.0",
+ "@fluentui/react-color-picker": "^9.2.15",
+ "@fluentui/react-combobox": "^9.17.0",
+ "@fluentui/react-dialog": "^9.17.3",
+ "@fluentui/react-divider": "^9.7.0",
+ "@fluentui/react-drawer": "^9.11.6",
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-image": "^9.4.0",
+ "@fluentui/react-infobutton": "9.0.0-beta.114",
+ "@fluentui/react-infolabel": "^9.4.19",
+ "@fluentui/react-input": "^9.8.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-link": "^9.8.0",
+ "@fluentui/react-list": "^9.6.13",
+ "@fluentui/react-menu": "^9.24.0",
+ "@fluentui/react-message-bar": "^9.6.23",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-nav": "^9.3.23",
+ "@fluentui/react-overflow": "^9.7.1",
+ "@fluentui/react-persona": "^9.7.2",
+ "@fluentui/react-popover": "^9.14.1",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-positioning": "^9.22.0",
+ "@fluentui/react-progress": "^9.5.0",
+ "@fluentui/react-provider": "^9.22.15",
+ "@fluentui/react-radio": "^9.6.1",
+ "@fluentui/react-rating": "^9.4.0",
+ "@fluentui/react-search": "^9.4.1",
+ "@fluentui/react-select": "^9.5.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-skeleton": "^9.7.1",
+ "@fluentui/react-slider": "^9.6.1",
+ "@fluentui/react-spinbutton": "^9.6.1",
+ "@fluentui/react-spinner": "^9.8.1",
+ "@fluentui/react-swatch-picker": "^9.5.1",
+ "@fluentui/react-switch": "^9.7.1",
+ "@fluentui/react-table": "^9.19.14",
+ "@fluentui/react-tabs": "^9.12.0",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-tag-picker": "^9.8.5",
+ "@fluentui/react-tags": "^9.8.0",
+ "@fluentui/react-teaching-popover": "^9.6.20",
+ "@fluentui/react-text": "^9.6.15",
+ "@fluentui/react-textarea": "^9.7.1",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-toast": "^9.7.16",
+ "@fluentui/react-toolbar": "^9.7.7",
+ "@fluentui/react-tooltip": "^9.10.0",
+ "@fluentui/react-tree": "^9.15.16",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@fluentui/react-virtualizer": "9.0.0-alpha.111",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-context-selector": {
- "version": "9.2.6",
- "resolved": "https://registry.npmjs.org/@fluentui/react-context-selector/-/react-context-selector-9.2.6.tgz",
- "integrity": "sha512-AskFoj248mH8USB/GfXRxj4PbVETVg+T1Xl+uVS6owYchVqkDDHW3oYnZdOTY/rMf1hxOUJhcC3GtXP0JRFdbg==",
+ "version": "9.2.15",
"license": "MIT",
"dependencies": {
- "@fluentui/react-utilities": "^9.24.0",
+ "@fluentui/react-utilities": "^9.26.2",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0",
- "scheduler": ">=0.19.0 <=0.23.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0",
+ "scheduler": ">=0.19.0"
}
},
"node_modules/@fluentui/react-dialog": {
- "version": "9.15.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-dialog/-/react-dialog-9.15.0.tgz",
- "integrity": "sha512-sB8ilho8af0QW+pekkBJRpXaZvh1CQkEUOUdB0UhGWlH0zuRdl3gbMujjh06anVJgeo6bT2yomlG2YPjVLv9Rg==",
+ "version": "9.17.3",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-motion-components-preview": "^0.9.0",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-divider": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-divider/-/react-divider-9.4.4.tgz",
- "integrity": "sha512-Hg61v5YSh02H/fQJdfkzpqkrrupXIdzfbnRczCsjl5r9W2sqlO0STC100/SCmxtLoZN5208tM268NIPGfQLArw==",
+ "version": "9.7.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-drawer": {
- "version": "9.10.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-drawer/-/react-drawer-9.10.0.tgz",
- "integrity": "sha512-yoTJGoH6jgL2/Nu3wfJptbMZdGnHhUh4cOKESTiiSjCmVgmr56gGFzMjAICek1YLtrnxGBEAJngkOpyQFNHQtw==",
- "license": "MIT",
- "dependencies": {
- "@fluentui/react-dialog": "^9.15.0",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "version": "9.11.6",
+ "license": "MIT",
+ "dependencies": {
+ "@fluentui/react-dialog": "^9.17.3",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-field": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-field/-/react-field-9.4.4.tgz",
- "integrity": "sha512-JtW3faTdKIE/d/mum9ZDkiC6vyip7h5rLa7zhIQ/Eek0JR2vHZwta8BODxY0Mwvga/xTK9aC3fNo/FcXSoL3Rg==",
+ "version": "9.5.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-icons": {
- "version": "2.0.309",
- "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.309.tgz",
- "integrity": "sha512-rxR1iTh7FfVuFzyaLym0NLzAkfR+dVo2M53qv1uISYUvoZUGoTUazECTPmRXnMb33vtHuf6VT/quQyhCrLCmlA==",
+ "version": "2.0.323",
+ "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.323.tgz",
+ "integrity": "sha512-BWFvdg8Er3668fri7o5RVqdfDO3jIg0OvJmUl5EWg6lO7TeC8A+OTggjzqO+J062ONaHPHpQ9IHbnYQ+QXGwXg==",
"license": "MIT",
"dependencies": {
- "@griffel/react": "^1.0.0",
+ "@griffel/react": "^1.6.1",
"tslib": "^2.1.0"
},
"peerDependencies": {
- "react": ">=16.8.0 <19.0.0"
+ "react": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-image": {
- "version": "9.3.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-image/-/react-image-9.3.4.tgz",
- "integrity": "sha512-wtRE7D+1Td9Ha5asRxDuUCIGfx75ilIWgZDws2MQoZrVo05iSAf3F+Ylv+MuiQ2p8N46n8gGyUBNmyFwfWUfKA==",
+ "version": "9.4.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-infobutton": {
- "version": "9.0.0-beta.102",
- "resolved": "https://registry.npmjs.org/@fluentui/react-infobutton/-/react-infobutton-9.0.0-beta.102.tgz",
- "integrity": "sha512-3kA4F0Vga8Ds6JGlBajLCCDOo/LmPuS786Wg7ui4ZTDYVIMzy1yp2XuVcZniifBFvEp0HQCUoDPWUV0VI3FfzQ==",
+ "version": "9.0.0-beta.114",
"license": "MIT",
"dependencies": {
"@fluentui/react-icons": "^2.0.237",
- "@fluentui/react-jsx-runtime": "^9.0.36",
- "@fluentui/react-label": "^9.1.68",
- "@fluentui/react-popover": "^9.9.6",
- "@fluentui/react-tabster": "^9.21.0",
- "@fluentui/react-theme": "^9.1.19",
- "@fluentui/react-utilities": "^9.18.7",
- "@griffel/react": "^1.5.14",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-popover": "^9.14.1",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-infolabel": {
- "version": "9.4.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-infolabel/-/react-infolabel-9.4.5.tgz",
- "integrity": "sha512-sjUPSt1VeBkvHIn+Iq3LL+KXwrzLGANkR2MC80+OJNn59tk3jVFkcnlPxWYWnOD/Zlpl6SqIlKnzrVQGfIxxvA==",
+ "version": "9.4.19",
"license": "MIT",
"dependencies": {
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-popover": "^9.12.5",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-popover": "^9.14.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-input": {
- "version": "9.7.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-input/-/react-input-9.7.4.tgz",
- "integrity": "sha512-ZNhM5xKckA39O3g6LjwoZCqy8kopFQ1ujfwxl0D60fEDMBwUYoK2NR1Zr/pEF9ItuhKlIN9fs1F/Hqay7fnYDw==",
+ "version": "9.8.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-jsx-runtime": {
- "version": "9.1.6",
- "resolved": "https://registry.npmjs.org/@fluentui/react-jsx-runtime/-/react-jsx-runtime-9.1.6.tgz",
- "integrity": "sha512-ClaksavUB9CPRPuMKxtsjVCg+N95jMt3Oi5RBGY4dAMxwaERpweQPv5CCuZzOq4Ybp4FpAXwK1jGNZzXizvfaA==",
+ "version": "9.4.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-utilities": "^9.24.0",
- "@swc/helpers": "^0.5.1",
- "react-is": "^17.0.2"
+ "@fluentui/react-utilities": "^9.26.2",
+ "@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-label": {
- "version": "9.3.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-label/-/react-label-9.3.4.tgz",
- "integrity": "sha512-oBdN3J5qFuiS57eCk+rXEYg+zt/7Mgt7SqxQlJzkU8uzlj5J5B+IjITlADOEYjuG0QDzhNA4/et2AX8c8kA55Q==",
+ "version": "9.4.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-link": {
- "version": "9.6.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-link/-/react-link-9.6.4.tgz",
- "integrity": "sha512-jmn/lkDt31bE8ZMgPQ9ZCeUeHJ7fL28HelOj8Mod9lhTfykyFESzWjd3oJQ0FSKta5I1oqwrBcxa4dIuDM2sfw==",
+ "version": "9.8.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-list": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-list/-/react-list-9.5.0.tgz",
- "integrity": "sha512-iJIq5DNxRDog2AFror7d/7q7mzTcVnjejfF4ZhpIZW0hYOzpeVsZvCWilvg96ItvXgNApM3F369ZLLs1Q3uUIQ==",
+ "version": "9.6.13",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-checkbox": "^9.5.4",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-checkbox": "^9.6.0",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-menu": {
- "version": "9.19.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-menu/-/react-menu-9.19.5.tgz",
- "integrity": "sha512-+tvO4m8DB0NBPnFedcpCvmNJVmC/6VQd2Gzn8VIqJOBVnm1xRQ85YjH7d8CK1FKdW26JhYAAj8pVIh8k+mLseA==",
+ "version": "9.24.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-positioning": "^9.20.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-positioning": "^9.22.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-message-bar": {
- "version": "9.6.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-message-bar/-/react-message-bar-9.6.5.tgz",
- "integrity": "sha512-YpCaYxN4Y0sFalk1GZ1L4MXSGLepvyON9uW1PVeWS89XQlWGPCSSEhFTUjWrQJar2wsJ8kv/LKreQb87mCYolg==",
+ "version": "9.6.23",
"license": "MIT",
"dependencies": {
- "@fluentui/react-button": "^9.6.5",
+ "@fluentui/react-button": "^9.9.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-link": "^9.6.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
- "@swc/helpers": "^0.5.1",
- "react-transition-group": "^4.4.1"
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-link": "^9.8.0",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
+ "@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-motion": {
- "version": "9.10.3",
- "resolved": "https://registry.npmjs.org/@fluentui/react-motion/-/react-motion-9.10.3.tgz",
- "integrity": "sha512-0UZyBSY73wP+p2s8FQsi4XdBCuGzjZ5MXy/2oohqX3yAb8t+F7e1ID0fJym9pnwwYkGeugZUlkWfyWgFPuSQag==",
+ "version": "9.14.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-utilities": "^9.24.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-utilities": "^9.26.2",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-motion-components-preview": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-motion-components-preview/-/react-motion-components-preview-0.9.0.tgz",
- "integrity": "sha512-MkzDBtuZzFCW9RC7zW9e7r8AdcocpGigMQpL6gi9OYYEUDiIPSjTsitok9W0ZZ7H4gBy+p7MjG/we5JcsBCnpQ==",
+ "version": "0.15.3",
"license": "MIT",
"dependencies": {
"@fluentui/react-motion": "*",
+ "@fluentui/react-utilities": "*",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-nav": {
- "version": "9.3.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-nav/-/react-nav-9.3.5.tgz",
- "integrity": "sha512-SumdUakSW1XWmzJG7OsiNuJDAhxHWa+uNvZ/rURJTFGkwSt+a1Fi0UL1uutyMtK1U5rCBRMtrf79r3M3+DURJw==",
+ "version": "9.3.23",
"license": "MIT",
"dependencies": {
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-button": "^9.6.5",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-divider": "^9.4.4",
- "@fluentui/react-drawer": "^9.10.0",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-button": "^9.9.0",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-divider": "^9.7.0",
+ "@fluentui/react-drawer": "^9.11.6",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-tooltip": "^9.8.4",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-tooltip": "^9.10.0",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-overflow": {
- "version": "9.5.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-overflow/-/react-overflow-9.5.5.tgz",
- "integrity": "sha512-WbG0DMJ5B7hOIYncmXjG1odS37mlldPpqm4WXpDv2IMIYzzlcI8JDk0KimrAb2/FgLrRm3vWbxZ1hyb5YjImrg==",
+ "version": "9.7.1",
"license": "MIT",
"dependencies": {
- "@fluentui/priority-overflow": "^9.1.15",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/priority-overflow": "^9.3.0",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-persona": {
- "version": "9.5.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-persona/-/react-persona-9.5.5.tgz",
- "integrity": "sha512-s//UCtV+Vf+/ghY3+InWph1mLOOG3NxhoRzttXDSfinzLXgDzf6PUPd+FbntK8eu6RyOllnquydnLTkDLt/k/g==",
- "license": "MIT",
- "dependencies": {
- "@fluentui/react-avatar": "^9.9.5",
- "@fluentui/react-badge": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "version": "9.7.2",
+ "license": "MIT",
+ "dependencies": {
+ "@fluentui/react-avatar": "^9.11.0",
+ "@fluentui/react-badge": "^9.5.1",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-popover": {
- "version": "9.12.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-popover/-/react-popover-9.12.5.tgz",
- "integrity": "sha512-GzIkJoyzRmgz8UgVq2xhqii/trIAMLpLYbr3XrxukrkDg837OZKFcBbSbqTUSNVZ6ra4RrlGMaF4yhWHBTSs1A==",
+ "version": "9.14.1",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-positioning": "^9.20.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-positioning": "^9.22.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-portal": {
- "version": "9.8.1",
- "resolved": "https://registry.npmjs.org/@fluentui/react-portal/-/react-portal-9.8.1.tgz",
- "integrity": "sha512-PjcKGNpphryhHBtlObbBVNrsasPt6QCbTyLYfmUKR92+XQI0U92AV9fHS7sArXGP3HrXjzUDvf+rLnecRMQmcA==",
+ "version": "9.8.11",
"license": "MIT",
"dependencies": {
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-positioning": {
- "version": "9.20.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-positioning/-/react-positioning-9.20.4.tgz",
- "integrity": "sha512-MyldPBLO+hX0+qI2kfRZRI1hdSihgDKqpdqkl6O25PVce2SaGvvDAK72GDNOyoAApnXlVOFIEAyLSWzxjTGDbw==",
+ "version": "9.22.0",
"license": "MIT",
"dependencies": {
"@floating-ui/devtools": "^0.2.3",
"@floating-ui/dom": "^1.6.12",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1",
"use-sync-external-store": "^1.2.0"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-progress": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-progress/-/react-progress-9.4.4.tgz",
- "integrity": "sha512-53oBCjgnqKLhX3amF8UczzBajOn1iQ1li4e14IIo+pmocI6kqohUWEBX6FUyor9+gSoty47pmS1T8izxyqnaCA==",
+ "version": "9.5.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-provider": {
- "version": "9.22.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-provider/-/react-provider-9.22.4.tgz",
- "integrity": "sha512-GhNGnFtNue7ZDxZjln4NtZMon0WNgaVBwEeqk2f5v6yzaGQN6Qm6/Ke/oCVTv++weimk2Sxysy2iN+/fMG3w0Q==",
+ "version": "9.22.15",
"license": "MIT",
"dependencies": {
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
"@griffel/core": "^1.16.0",
- "@griffel/react": "^1.5.22",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-radio": {
- "version": "9.5.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-radio/-/react-radio-9.5.4.tgz",
- "integrity": "sha512-wgqNgEMUbDmiSSNG8rtYYLVmkfABZyotTGAlyUMAsE4mw4wlcsLEFhVL2LNckH4a4DR/jeJb5McatgdpX7T4+Q==",
- "license": "MIT",
- "dependencies": {
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "version": "9.6.1",
+ "license": "MIT",
+ "dependencies": {
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-rating": {
- "version": "9.3.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-rating/-/react-rating-9.3.4.tgz",
- "integrity": "sha512-Nq1dp7tVxTPJ8arqPaQKW9Apw7clkqVH6zZc/9ssSqEQO4ap4pWZPY0omSkxwdk15jH0AKzXMGTN5eT9MfK8Kw==",
+ "version": "9.4.0",
"license": "MIT",
"dependencies": {
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-search": {
- "version": "9.3.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-search/-/react-search-9.3.4.tgz",
- "integrity": "sha512-l3JK18E+VQ+zZ0u9Id+xr3b1+KS8bWRVqbhU5Cm/BdtipW0pr/uzG8i5IH64pPLu9S0hfI4ROCQ2miZ5bBmO4g==",
+ "version": "9.4.1",
"license": "MIT",
"dependencies": {
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-input": "^9.7.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-input": "^9.8.1",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-select": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-select/-/react-select-9.4.4.tgz",
- "integrity": "sha512-NFAaZ1kMrMLNOqKlxkgIW66rO8RCNG3PRwbPBvHkMawupoFSiHag5r7YLxZsn1OX8HFnXz9wp083ZjWXHvEwWA==",
+ "version": "9.5.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-shared-contexts": {
- "version": "9.25.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-shared-contexts/-/react-shared-contexts-9.25.0.tgz",
- "integrity": "sha512-uFWi93L5ZjZACx5VA4+gbWgg6l/on3ultJpXTyFYFuox0paJbqENsPf383GKZW7UnUs08Kqry5CFC36VfqDdSg==",
+ "version": "9.26.2",
"license": "MIT",
"dependencies": {
- "@fluentui/react-theme": "^9.2.0",
+ "@fluentui/react-theme": "^9.2.1",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-skeleton": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-skeleton/-/react-skeleton-9.4.4.tgz",
- "integrity": "sha512-keXTUdweqPMffECCLoc2Fu35xxpLUNh3opGy4/ShT73YVTQgLyRTJMKv5v+y2TzujWP9T/THm+HHxe56eQBrVQ==",
+ "version": "9.7.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-slider": {
- "version": "9.5.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-slider/-/react-slider-9.5.4.tgz",
- "integrity": "sha512-AX6t49OMF/OWDN6M+gsBUu5ZAuhswLdvrnuRJY+jMHWSMitTK2DBgruNUKhpA1K5Kl0ZqFHlU8eTMti8FT6Nog==",
- "license": "MIT",
- "dependencies": {
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "version": "9.6.1",
+ "license": "MIT",
+ "dependencies": {
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-spinbutton": {
- "version": "9.5.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-spinbutton/-/react-spinbutton-9.5.4.tgz",
- "integrity": "sha512-MiNih2+ds5acPXNLYufvD9pnD6z2pZH0OHATrCh6MngAdbSTC5vR2+lP9qvBj02zQ/L4nZEcuaLbd4BrP7KUpg==",
+ "version": "9.6.1",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-spinner": {
- "version": "9.7.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-spinner/-/react-spinner-9.7.4.tgz",
- "integrity": "sha512-d4HTD4TlvM4PN+J5iWOrGqcfOyoPbX+KEQbUexX/4ZBNcGPsAbHtLH4IHoQTZIYUKRurLZH1dnTgyeTjraR2HQ==",
+ "version": "9.8.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-swatch-picker": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-swatch-picker/-/react-swatch-picker-9.4.4.tgz",
- "integrity": "sha512-U0xZRd9v4C/fwlx7ux4ufY2OWCnLzClqc97r+Roeg+5FCF3ACEwocwQoA/Md/uQxqVjeIMTyxW20Ozlk4rnLYQ==",
+ "version": "9.5.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-switch": {
- "version": "9.4.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-switch/-/react-switch-9.4.4.tgz",
- "integrity": "sha512-9DyAGW5L/cmxp6R9HNmP4SoSlzdf9oO7Z3Hbu5DoMHKTvL3hU86K84MeU1fNaDbHEkdgdVFMYt5QFbzoW/lkqw==",
+ "version": "9.7.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-label": "^9.3.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-label": "^9.4.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-table": {
- "version": "9.18.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-table/-/react-table-9.18.5.tgz",
- "integrity": "sha512-JQy7HiHiMkfi0H8u/cKui8mhRc3ESuClGSS2IRoGyCDPILRuwf1OW6h6uPMTf5DYJV5OnEwxQTM8zAjPTmZH1g==",
+ "version": "9.19.14",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-avatar": "^9.9.5",
- "@fluentui/react-checkbox": "^9.5.4",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-avatar": "^9.11.0",
+ "@fluentui/react-checkbox": "^9.6.0",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-radio": "^9.5.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-radio": "^9.6.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-tabs": {
- "version": "9.10.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-tabs/-/react-tabs-9.10.0.tgz",
- "integrity": "sha512-fFHAXmOwz+ESt23CKgicvu76FzVYywcCj+/nL8xjMtulEnoNrKC1SkLwScTgeJgo+WQw2RchyG1fdFppPVz+zA==",
+ "version": "9.12.0",
"license": "MIT",
"dependencies": {
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-tabster": {
- "version": "9.26.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-tabster/-/react-tabster-9.26.4.tgz",
- "integrity": "sha512-ri/h4MHdSdTPn40isPZw1tOnB4W+wLj0EtJWDdKc49vDX8NXTmULLBDodHDsqauVJpKMw3Jw69Ccuf09S+qhTA==",
+ "version": "9.26.13",
"license": "MIT",
"dependencies": {
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1",
"keyborg": "^2.6.0",
"tabster": "^8.5.5"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-tag-picker": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-tag-picker/-/react-tag-picker-9.7.5.tgz",
- "integrity": "sha512-0FlRcHhk08q1fR6YkUNShqSPT+Cq9LPsTVU2nlwk0piVY2BxTbCYD+lK+qjJmJHIXUtOA1naQESRdQMmrStfYA==",
+ "version": "9.8.5",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-combobox": "^9.16.5",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-field": "^9.4.4",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-combobox": "^9.17.0",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-field": "^9.5.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-positioning": "^9.20.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-tags": "^9.7.5",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-positioning": "^9.22.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-tags": "^9.8.0",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-tags": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-tags/-/react-tags-9.7.5.tgz",
- "integrity": "sha512-9rJv6bHzMsEvmWJFIUwq1bgLZ7D1XZ556fOtPl9P7JU2i6gCYzkXCakHm9faUJnNw2CcKq0aw38sGJoHR7wNuA==",
+ "version": "9.8.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-avatar": "^9.9.5",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-avatar": "^9.11.0",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-teaching-popover": {
- "version": "9.6.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-teaching-popover/-/react-teaching-popover-9.6.5.tgz",
- "integrity": "sha512-fNSwEXRPDa5qRjgEI8vvlki279/hhCWeQyYFyJ4D4pRga8u3CGa6RI33GuUsxHO2ROOgMRFh2JJIYlG/+GMhjQ==",
+ "version": "9.6.20",
"license": "MIT",
"dependencies": {
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-button": "^9.6.5",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-button": "^9.9.0",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-popover": "^9.12.5",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-popover": "^9.14.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1",
"use-sync-external-store": "^1.2.0"
},
"peerDependencies": {
- "@types/react": ">=16.8.0 <19.0.0",
- "@types/react-dom": ">=16.8.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.8.0 <19.0.0"
+ "@types/react": ">=16.8.0 <20.0.0",
+ "@types/react-dom": ">=16.8.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@fluentui/react-text": {
- "version": "9.6.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-text/-/react-text-9.6.4.tgz",
- "integrity": "sha512-plHq9chCXcV9wtwNUtQYJSCTMJyEtMKHFj9s54ZS6GZOIxm/SIqsSz5ZAR25mgdn4mlyuMS+Ac3nBR83T+zVDw==",
+ "version": "9.6.15",
"license": "MIT",
"dependencies": {
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-textarea": {
- "version": "9.6.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-textarea/-/react-textarea-9.6.4.tgz",
- "integrity": "sha512-Gb6XkGNAiPE19cBfIkJVph3hKxubNrh5/idRQVDpQapjlRC2d8RmnNtUIlLwkiWtIdFvis0lxZuATQlDTQlnBA==",
+ "version": "9.7.1",
"license": "MIT",
"dependencies": {
- "@fluentui/react-field": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-field": "^9.5.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-theme": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-theme/-/react-theme-9.2.0.tgz",
- "integrity": "sha512-Q0zp/MY1m5RjlkcwMcjn/PQRT2T+q3bgxuxWbhgaD07V+tLzBhGROvuqbsdg4YWF/IK21zPfLhmGyifhEu0DnQ==",
+ "version": "9.2.1",
"license": "MIT",
"dependencies": {
- "@fluentui/tokens": "1.0.0-alpha.22",
+ "@fluentui/tokens": "1.0.0-alpha.23",
"@swc/helpers": "^0.5.1"
}
},
"node_modules/@fluentui/react-toast": {
- "version": "9.7.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-toast/-/react-toast-9.7.0.tgz",
- "integrity": "sha512-8GjhlUhKheDOEJudFCVCU9zFnXO66cAfn7xeMeIda5ZwdknD9Qh05bFLK68MRfBj9KpzfJC7tX84ztLDihVqzg==",
+ "version": "9.7.16",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
+ "@fluentui/react-aria": "^9.17.10",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-motion-components-preview": "^0.9.0",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-toolbar": {
- "version": "9.6.5",
- "resolved": "https://registry.npmjs.org/@fluentui/react-toolbar/-/react-toolbar-9.6.5.tgz",
- "integrity": "sha512-eHnZb2+/2AL0ZWO9dgm4IirXBgzFTCVEDT2oXMXNG49IbbZOrPo+MX+POb4gduKUdOE7STJvrgw79ePs+Q94hA==",
- "license": "MIT",
- "dependencies": {
- "@fluentui/react-button": "^9.6.5",
- "@fluentui/react-context-selector": "^9.2.6",
- "@fluentui/react-divider": "^9.4.4",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-radio": "^9.5.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "version": "9.7.7",
+ "license": "MIT",
+ "dependencies": {
+ "@fluentui/react-button": "^9.9.0",
+ "@fluentui/react-context-selector": "^9.2.15",
+ "@fluentui/react-divider": "^9.7.0",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-radio": "^9.6.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-tooltip": {
- "version": "9.8.4",
- "resolved": "https://registry.npmjs.org/@fluentui/react-tooltip/-/react-tooltip-9.8.4.tgz",
- "integrity": "sha512-Yb8kW37CmK2CI5zilYYnvVjeXKyH1S8Fdi5lXmL6sm48Vf/Ad5s8WKYGzTRq7faLN7oR2R53Z+t8g7EEGfhO2w==",
+ "version": "9.10.0",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-portal": "^9.8.1",
- "@fluentui/react-positioning": "^9.20.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-portal": "^9.8.11",
+ "@fluentui/react-positioning": "^9.22.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-tree": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-tree/-/react-tree-9.13.0.tgz",
- "integrity": "sha512-UJKiZyqtqE1c2ICtUSDuTVe1bZb+i5CVOZvQrgjNiSolRKAFrLEOk7G+wOjq6X4OPwiZRp+rpkHLr6KTJ3LFsg==",
+ "version": "9.15.16",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-aria": "^9.16.4",
- "@fluentui/react-avatar": "^9.9.5",
- "@fluentui/react-button": "^9.6.5",
- "@fluentui/react-checkbox": "^9.5.4",
- "@fluentui/react-context-selector": "^9.2.6",
+ "@fluentui/react-aria": "^9.17.10",
+ "@fluentui/react-avatar": "^9.11.0",
+ "@fluentui/react-button": "^9.9.0",
+ "@fluentui/react-checkbox": "^9.6.0",
+ "@fluentui/react-context-selector": "^9.2.15",
"@fluentui/react-icons": "^2.0.245",
- "@fluentui/react-jsx-runtime": "^9.1.6",
- "@fluentui/react-motion": "^9.10.3",
- "@fluentui/react-motion-components-preview": "^0.9.0",
- "@fluentui/react-radio": "^9.5.4",
- "@fluentui/react-shared-contexts": "^9.25.0",
- "@fluentui/react-tabster": "^9.26.4",
- "@fluentui/react-theme": "^9.2.0",
- "@fluentui/react-utilities": "^9.24.0",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-motion": "^9.14.0",
+ "@fluentui/react-motion-components-preview": "^0.15.3",
+ "@fluentui/react-radio": "^9.6.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-tabster": "^9.26.13",
+ "@fluentui/react-theme": "^9.2.1",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-utilities": {
- "version": "9.24.0",
- "resolved": "https://registry.npmjs.org/@fluentui/react-utilities/-/react-utilities-9.24.0.tgz",
- "integrity": "sha512-fIAEi62slg3YGe9nbUW4crD9KLx//eNWBVRuwEvhqJeqrbLL6dTWRAmRhmYOmzzySy+4gxHP7I/D7jl3BjeXpA==",
+ "version": "9.26.2",
"license": "MIT",
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.8",
- "@fluentui/react-shared-contexts": "^9.25.0",
+ "@fluentui/react-shared-contexts": "^9.26.2",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/react-virtualizer": {
- "version": "9.0.0-alpha.102",
- "resolved": "https://registry.npmjs.org/@fluentui/react-virtualizer/-/react-virtualizer-9.0.0-alpha.102.tgz",
- "integrity": "sha512-kt/kuAMTKTTY/00ToUlgUwUCty2HGj4Tnr+fxKRmr7Ziy5VWhi1YoNJ8vcgmxog5J90t4tS29LB0LP0KztQUVg==",
+ "version": "9.0.0-alpha.111",
"license": "MIT",
"dependencies": {
- "@fluentui/react-jsx-runtime": "^9.1.4",
- "@fluentui/react-shared-contexts": "^9.24.1",
- "@fluentui/react-utilities": "^9.23.1",
- "@griffel/react": "^1.5.22",
+ "@fluentui/react-jsx-runtime": "^9.4.1",
+ "@fluentui/react-shared-contexts": "^9.26.2",
+ "@fluentui/react-utilities": "^9.26.2",
+ "@griffel/react": "^1.5.32",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
- "@types/react": ">=16.14.0 <19.0.0",
- "@types/react-dom": ">=16.9.0 <19.0.0",
- "react": ">=16.14.0 <19.0.0",
- "react-dom": ">=16.14.0 <19.0.0"
+ "@types/react": ">=16.14.0 <20.0.0",
+ "@types/react-dom": ">=16.9.0 <20.0.0",
+ "react": ">=16.14.0 <20.0.0",
+ "react-dom": ">=16.14.0 <20.0.0"
}
},
"node_modules/@fluentui/set-version": {
"version": "8.2.24",
- "resolved": "https://registry.npmjs.org/@fluentui/set-version/-/set-version-8.2.24.tgz",
- "integrity": "sha512-8uNi2ThvNgF+6d3q2luFVVdk/wZV0AbRfJ85kkvf2+oSRY+f6QVK0w13vMorNhA5puumKcZniZoAfUF02w7NSg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@fluentui/tokens": {
- "version": "1.0.0-alpha.22",
- "resolved": "https://registry.npmjs.org/@fluentui/tokens/-/tokens-1.0.0-alpha.22.tgz",
- "integrity": "sha512-i9fgYyyCWFRdUi+vQwnV6hp7wpLGK4p09B+O/f2u71GBXzPuniubPYvrIJYtl444DD6shLjYToJhQ1S6XTFwLg==",
+ "version": "1.0.0-alpha.23",
"license": "MIT",
"dependencies": {
"@swc/helpers": "^0.5.1"
}
},
"node_modules/@griffel/core": {
- "version": "1.19.2",
- "resolved": "https://registry.npmjs.org/@griffel/core/-/core-1.19.2.tgz",
- "integrity": "sha512-WkB/QQkjy9dE4vrNYGhQvRRUHFkYVOuaznVOMNTDT4pS9aTJ9XPrMTXXlkpcwaf0D3vNKoerj4zAwnU2lBzbOg==",
+ "version": "1.20.1",
"license": "MIT",
"dependencies": {
"@emotion/hash": "^0.9.0",
- "@griffel/style-types": "^1.3.0",
+ "@griffel/style-types": "^1.4.0",
"csstype": "^3.1.3",
"rtl-css-js": "^1.16.1",
"stylis": "^4.2.0",
@@ -2619,12 +2410,10 @@
}
},
"node_modules/@griffel/react": {
- "version": "1.5.30",
- "resolved": "https://registry.npmjs.org/@griffel/react/-/react-1.5.30.tgz",
- "integrity": "sha512-1q4ojbEVFY5YA0j1NamP0WWF4BKh+GHsVugltDYeEgEaVbH3odJ7tJabuhQgY+7Nhka0pyEFWSiHJev0K3FSew==",
+ "version": "1.6.1",
"license": "MIT",
"dependencies": {
- "@griffel/core": "^1.19.2",
+ "@griffel/core": "^1.20.1",
"tslib": "^2.1.0"
},
"peerDependencies": {
@@ -2632,9 +2421,7 @@
}
},
"node_modules/@griffel/style-types": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@griffel/style-types/-/style-types-1.3.0.tgz",
- "integrity": "sha512-bHwD3sUE84Xwv4dH011gOKe1jul77M1S6ZFN9Tnq8pvZ48UMdY//vtES6fv7GRS5wXYT4iqxQPBluAiYAfkpmw==",
+ "version": "1.4.0",
"license": "MIT",
"dependencies": {
"csstype": "^3.1.3"
@@ -2642,9 +2429,6 @@
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
- "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
- "deprecated": "Use @eslint/config-array instead",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -2658,8 +2442,6 @@
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -2672,16 +2454,11 @@
},
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
- "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
- "deprecated": "Use @eslint/object-schema instead",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2691,8 +2468,6 @@
},
"node_modules/@jridgewell/remapping": {
"version": "2.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
- "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2702,8 +2477,6 @@
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2712,15 +2485,11 @@
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.30",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
- "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "version": "0.3.31",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2730,8 +2499,6 @@
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2744,8 +2511,6 @@
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2754,8 +2519,6 @@
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2768,22 +2531,42 @@
},
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
- "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
- "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
"dev": true,
"license": "MIT"
},
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.11.2",
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@standard-schema/utils": "^0.3.0",
+ "immer": "^11.0.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.27",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
- "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz",
- "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
+ "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==",
"cpu": [
"arm"
],
@@ -2795,9 +2578,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz",
- "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz",
+ "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==",
"cpu": [
"arm64"
],
@@ -2809,9 +2592,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz",
- "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz",
+ "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==",
"cpu": [
"arm64"
],
@@ -2823,9 +2606,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz",
- "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz",
+ "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==",
"cpu": [
"x64"
],
@@ -2837,9 +2620,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz",
- "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz",
+ "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==",
"cpu": [
"arm64"
],
@@ -2851,9 +2634,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz",
- "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz",
+ "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==",
"cpu": [
"x64"
],
@@ -2865,9 +2648,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz",
- "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz",
+ "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==",
"cpu": [
"arm"
],
@@ -2879,9 +2662,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz",
- "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz",
+ "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==",
"cpu": [
"arm"
],
@@ -2893,9 +2676,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz",
- "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz",
+ "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==",
"cpu": [
"arm64"
],
@@ -2907,9 +2690,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz",
- "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz",
+ "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==",
"cpu": [
"arm64"
],
@@ -2920,10 +2703,24 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz",
- "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==",
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz",
+ "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz",
+ "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==",
"cpu": [
"loong64"
],
@@ -2935,9 +2732,23 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz",
- "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz",
+ "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz",
+ "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==",
"cpu": [
"ppc64"
],
@@ -2949,9 +2760,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz",
- "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz",
+ "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==",
"cpu": [
"riscv64"
],
@@ -2963,9 +2774,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz",
- "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz",
+ "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==",
"cpu": [
"riscv64"
],
@@ -2977,9 +2788,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz",
- "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz",
+ "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==",
"cpu": [
"s390x"
],
@@ -2991,12 +2802,13 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.40.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz",
- "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz",
+ "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==",
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3004,9 +2816,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz",
- "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz",
+ "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==",
"cpu": [
"x64"
],
@@ -3017,10 +2829,24 @@
"linux"
]
},
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz",
+ "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz",
- "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz",
+ "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==",
"cpu": [
"arm64"
],
@@ -3032,9 +2858,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz",
- "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz",
+ "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==",
"cpu": [
"arm64"
],
@@ -3046,9 +2872,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz",
- "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==",
+ "version": "4.60.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz",
+ "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==",
"cpu": [
"ia32"
],
@@ -3059,10 +2885,20 @@
"win32"
]
},
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.60.1",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz",
- "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==",
+ "version": "4.60.1",
"cpu": [
"x64"
],
@@ -3073,10 +2909,16 @@
"win32"
]
},
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/utils": {
+ "version": "0.3.0",
+ "license": "MIT"
+ },
"node_modules/@swc/helpers": {
- "version": "0.5.17",
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
- "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
+ "version": "0.5.21",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.8.0"
@@ -3084,8 +2926,6 @@
},
"node_modules/@testing-library/dom": {
"version": "10.4.1",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
- "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
@@ -3102,9 +2942,7 @@
}
},
"node_modules/@testing-library/jest-dom": {
- "version": "6.8.0",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz",
- "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==",
+ "version": "6.9.1",
"license": "MIT",
"dependencies": {
"@adobe/css-tools": "^4.4.0",
@@ -3122,14 +2960,10 @@
},
"node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
"version": "0.6.3",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
- "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
"license": "MIT"
},
"node_modules/@testing-library/react": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz",
- "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==",
+ "version": "16.3.2",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
@@ -3155,8 +2989,6 @@
},
"node_modules/@testing-library/user-event": {
"version": "13.5.0",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
- "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
@@ -3171,14 +3003,10 @@
},
"node_modules/@types/aria-query": {
"version": "5.0.4",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
- "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"license": "MIT"
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
- "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3191,8 +3019,6 @@
},
"node_modules/@types/babel__generator": {
"version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
- "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3201,8 +3027,6 @@
},
"node_modules/@types/babel__template": {
"version": "7.4.4",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
- "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3212,8 +3036,6 @@
},
"node_modules/@types/babel__traverse": {
"version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
- "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3221,19 +3043,16 @@
}
},
"node_modules/@types/chai": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
- "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
+ "version": "5.2.3",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/deep-eql": "*"
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
}
},
"node_modules/@types/debug": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
- "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "version": "4.1.13",
"license": "MIT",
"dependencies": {
"@types/ms": "*"
@@ -3241,21 +3060,15 @@
},
"node_modules/@types/deep-eql": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT"
},
"node_modules/@types/estree-jsx": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
- "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
"license": "MIT",
"dependencies": {
"@types/estree": "*"
@@ -3263,8 +3076,6 @@
},
"node_modules/@types/hast": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"license": "MIT",
"dependencies": {
"@types/unist": "*"
@@ -3272,8 +3083,6 @@
},
"node_modules/@types/jest": {
"version": "27.5.2",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz",
- "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==",
"license": "MIT",
"dependencies": {
"jest-matcher-utils": "^27.0.0",
@@ -3282,15 +3091,11 @@
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/mdast": {
"version": "4.0.4",
- "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
- "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
"license": "MIT",
"dependencies": {
"@types/unist": "*"
@@ -3298,14 +3103,10 @@
},
"node_modules/@types/ms": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
- "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "20.19.13",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz",
- "integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==",
+ "version": "20.19.39",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3314,24 +3115,18 @@
},
"node_modules/@types/prop-types": {
"version": "15.7.15",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
- "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "18.3.24",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz",
- "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
+ "version": "18.3.28",
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
"version": "18.3.7",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
- "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0"
@@ -3339,21 +3134,19 @@
},
"node_modules/@types/semver": {
"version": "7.7.1",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
- "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/unist": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.6",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
- "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3387,8 +3180,6 @@
},
"node_modules/@typescript-eslint/parser": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
- "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -3415,8 +3206,6 @@
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
- "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3433,8 +3222,6 @@
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
- "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3461,8 +3248,6 @@
},
"node_modules/@typescript-eslint/types": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
- "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3475,8 +3260,6 @@
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
- "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -3503,8 +3286,6 @@
},
"node_modules/@typescript-eslint/utils": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
- "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3530,8 +3311,6 @@
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
- "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3548,14 +3327,10 @@
},
"node_modules/@ungap/structured-clone": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
- "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"license": "ISC"
},
"node_modules/@vitejs/plugin-react": {
"version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
- "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3575,8 +3350,6 @@
},
"node_modules/@vitest/expect": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
- "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3592,8 +3365,6 @@
},
"node_modules/@vitest/mocker": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
- "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3619,8 +3390,6 @@
},
"node_modules/@vitest/pretty-format": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
- "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3632,8 +3401,6 @@
},
"node_modules/@vitest/runner": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
- "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3647,8 +3414,6 @@
},
"node_modules/@vitest/snapshot": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
- "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3662,8 +3427,6 @@
},
"node_modules/@vitest/spy": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
- "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3675,8 +3438,6 @@
},
"node_modules/@vitest/ui": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.2.4.tgz",
- "integrity": "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3697,8 +3458,6 @@
},
"node_modules/@vitest/utils": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
- "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3711,9 +3470,7 @@
}
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
"dev": true,
"license": "MIT",
"bin": {
@@ -3725,8 +3482,6 @@
},
"node_modules/acorn-jsx": {
"version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -3735,8 +3490,6 @@
},
"node_modules/agent-base": {
"version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3744,9 +3497,7 @@
}
},
"node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "version": "6.14.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3762,8 +3513,6 @@
},
"node_modules/ansi-regex": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3771,8 +3520,6 @@
},
"node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -3786,15 +3533,11 @@
},
"node_modules/argparse": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/aria-query": {
"version": "5.3.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
- "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"license": "Apache-2.0",
"dependencies": {
"dequal": "^2.0.3"
@@ -3802,8 +3545,6 @@
},
"node_modules/array-buffer-byte-length": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
- "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3819,8 +3560,6 @@
},
"node_modules/array-includes": {
"version": "3.1.9",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
- "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3842,8 +3581,6 @@
},
"node_modules/array-union": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3852,8 +3589,6 @@
},
"node_modules/array.prototype.findlast": {
"version": "1.2.5",
- "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
- "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3873,8 +3608,6 @@
},
"node_modules/array.prototype.flat": {
"version": "1.3.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
- "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3892,8 +3625,6 @@
},
"node_modules/array.prototype.flatmap": {
"version": "1.3.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
- "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3911,8 +3642,6 @@
},
"node_modules/array.prototype.tosorted": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
- "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3928,8 +3657,6 @@
},
"node_modules/arraybuffer.prototype.slice": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
- "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3950,8 +3677,6 @@
},
"node_modules/assertion-error": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3960,24 +3685,14 @@
},
"node_modules/async-function": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
- "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "license": "MIT"
- },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
- "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3990,21 +3705,8 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/axios": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
- "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
- "license": "MIT",
- "dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.4",
- "proxy-from-env": "^1.1.0"
- }
- },
"node_modules/bail": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
- "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4013,21 +3715,26 @@
},
"node_modules/balanced-match": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.16",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/boolbase": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
"node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.13",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4037,8 +3744,6 @@
},
"node_modules/braces": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4049,9 +3754,7 @@
}
},
"node_modules/browserslist": {
- "version": "4.25.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
- "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
+ "version": "4.28.2",
"dev": true,
"funding": [
{
@@ -4069,10 +3772,11 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001737",
- "electron-to-chromium": "^1.5.211",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.3"
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
@@ -4083,8 +3787,6 @@
},
"node_modules/cac": {
"version": "6.7.14",
- "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
- "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4093,8 +3795,6 @@
},
"node_modules/call-bind": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
- "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4112,8 +3812,7 @@
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -4125,8 +3824,6 @@
},
"node_modules/call-bound": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4142,8 +3839,6 @@
},
"node_modules/callsites": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4151,9 +3846,7 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001741",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz",
- "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==",
+ "version": "1.0.30001787",
"dev": true,
"funding": [
{
@@ -4173,8 +3866,6 @@
},
"node_modules/ccount": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
- "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4183,8 +3874,6 @@
},
"node_modules/chai": {
"version": "5.3.3",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
- "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4200,8 +3889,6 @@
},
"node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
@@ -4216,8 +3903,6 @@
},
"node_modules/character-entities": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
- "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4226,8 +3911,6 @@
},
"node_modules/character-entities-html4": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
- "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4236,8 +3919,6 @@
},
"node_modules/character-entities-legacy": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
- "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4246,8 +3927,6 @@
},
"node_modules/character-reference-invalid": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
- "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4255,9 +3934,7 @@
}
},
"node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "version": "2.1.3",
"dev": true,
"license": "MIT",
"engines": {
@@ -4266,8 +3943,6 @@
},
"node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -4278,26 +3953,10 @@
},
"node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "license": "MIT",
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
- "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4306,31 +3965,27 @@
},
"node_modules/concat-map": {
"version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true,
"license": "MIT"
},
"node_modules/convert-source-map": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/cookie": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
- "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "version": "1.1.1",
"license": "MIT",
"engines": {
"node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4343,9 +3998,7 @@
}
},
"node_modules/css-selector-parser": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.1.3.tgz",
- "integrity": "sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==",
+ "version": "3.3.0",
"funding": [
{
"type": "github",
@@ -4360,14 +4013,10 @@
},
"node_modules/css.escape": {
"version": "1.5.1",
- "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
- "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
"license": "MIT"
},
"node_modules/cssstyle": {
"version": "4.6.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
- "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4379,15 +4028,11 @@
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
"license": "MIT"
},
"node_modules/data-urls": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
- "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4400,8 +4045,6 @@
},
"node_modules/data-view-buffer": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
- "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4418,8 +4061,6 @@
},
"node_modules/data-view-byte-length": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
- "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4436,8 +4077,6 @@
},
"node_modules/data-view-byte-offset": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
- "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4453,9 +4092,7 @@
}
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -4471,15 +4108,11 @@
},
"node_modules/decimal.js": {
"version": "10.6.0",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
- "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"dev": true,
"license": "MIT"
},
"node_modules/decode-named-character-reference": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
- "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"character-entities": "^2.0.0"
@@ -4491,8 +4124,6 @@
},
"node_modules/deep-eql": {
"version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4501,15 +4132,11 @@
},
"node_modules/deep-is": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true,
"license": "MIT"
},
"node_modules/define-data-property": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4526,8 +4153,6 @@
},
"node_modules/define-properties": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4542,19 +4167,8 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/dequal": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -4562,8 +4176,6 @@
},
"node_modules/devlop": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
- "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.0"
@@ -4575,8 +4187,6 @@
},
"node_modules/diff-sequences": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
- "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
"license": "MIT",
"engines": {
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
@@ -4584,8 +4194,6 @@
},
"node_modules/dir-glob": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4597,8 +4205,6 @@
},
"node_modules/doctrine": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4610,24 +4216,11 @@
},
"node_modules/dom-accessibility-api": {
"version": "0.5.16",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
- "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"license": "MIT"
},
- "node_modules/dom-helpers": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
- "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.8.7",
- "csstype": "^3.0.2"
- }
- },
"node_modules/dunder-proto": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -4639,22 +4232,16 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.214",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.214.tgz",
- "integrity": "sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==",
+ "version": "1.5.333",
"dev": true,
"license": "ISC"
},
"node_modules/embla-carousel": {
"version": "8.6.0",
- "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
- "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
"license": "MIT"
},
"node_modules/embla-carousel-autoplay": {
"version": "8.6.0",
- "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.6.0.tgz",
- "integrity": "sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==",
"license": "MIT",
"peerDependencies": {
"embla-carousel": "8.6.0"
@@ -4662,8 +4249,6 @@
},
"node_modules/embla-carousel-fade": {
"version": "8.6.0",
- "resolved": "https://registry.npmjs.org/embla-carousel-fade/-/embla-carousel-fade-8.6.0.tgz",
- "integrity": "sha512-qaYsx5mwCz72ZrjlsXgs1nKejSrW+UhkbOMwLgfRT7w2LtdEB03nPRI06GHuHv5ac2USvbEiX2/nAHctcDwvpg==",
"license": "MIT",
"peerDependencies": {
"embla-carousel": "8.6.0"
@@ -4671,8 +4256,6 @@
},
"node_modules/entities": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -4682,9 +4265,7 @@
}
},
"node_modules/es-abstract": {
- "version": "1.24.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
- "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "version": "1.24.2",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4752,8 +4333,7 @@
},
"node_modules/es-define-property": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -4761,35 +4341,33 @@
},
"node_modules/es-errors": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-iterator-helpers": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
- "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "version": "1.3.1",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
+ "call-bound": "^1.0.4",
"define-properties": "^1.2.1",
- "es-abstract": "^1.23.6",
+ "es-abstract": "^1.24.1",
"es-errors": "^1.3.0",
- "es-set-tostringtag": "^2.0.3",
+ "es-set-tostringtag": "^2.1.0",
"function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.6",
+ "get-intrinsic": "^1.3.0",
"globalthis": "^1.0.4",
"gopd": "^1.2.0",
"has-property-descriptors": "^1.0.2",
"has-proto": "^1.2.0",
"has-symbols": "^1.1.0",
"internal-slot": "^1.1.0",
- "iterator.prototype": "^1.1.4",
+ "iterator.prototype": "^1.1.5",
+ "math-intrinsics": "^1.1.0",
"safe-array-concat": "^1.1.3"
},
"engines": {
@@ -4798,15 +4376,12 @@
},
"node_modules/es-module-lexer": {
"version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"dev": true,
"license": "MIT"
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -4817,8 +4392,7 @@
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
- "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -4832,8 +4406,6 @@
},
"node_modules/es-shim-unscopables": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
- "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4845,8 +4417,6 @@
},
"node_modules/es-to-primitive": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
- "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4862,9 +4432,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
- "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -4875,38 +4445,36 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.9",
- "@esbuild/android-arm": "0.25.9",
- "@esbuild/android-arm64": "0.25.9",
- "@esbuild/android-x64": "0.25.9",
- "@esbuild/darwin-arm64": "0.25.9",
- "@esbuild/darwin-x64": "0.25.9",
- "@esbuild/freebsd-arm64": "0.25.9",
- "@esbuild/freebsd-x64": "0.25.9",
- "@esbuild/linux-arm": "0.25.9",
- "@esbuild/linux-arm64": "0.25.9",
- "@esbuild/linux-ia32": "0.25.9",
- "@esbuild/linux-loong64": "0.25.9",
- "@esbuild/linux-mips64el": "0.25.9",
- "@esbuild/linux-ppc64": "0.25.9",
- "@esbuild/linux-riscv64": "0.25.9",
- "@esbuild/linux-s390x": "0.25.9",
- "@esbuild/linux-x64": "0.25.9",
- "@esbuild/netbsd-arm64": "0.25.9",
- "@esbuild/netbsd-x64": "0.25.9",
- "@esbuild/openbsd-arm64": "0.25.9",
- "@esbuild/openbsd-x64": "0.25.9",
- "@esbuild/openharmony-arm64": "0.25.9",
- "@esbuild/sunos-x64": "0.25.9",
- "@esbuild/win32-arm64": "0.25.9",
- "@esbuild/win32-ia32": "0.25.9",
- "@esbuild/win32-x64": "0.25.9"
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
}
},
"node_modules/escalade": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4915,8 +4483,6 @@
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4928,9 +4494,6 @@
},
"node_modules/eslint": {
"version": "8.57.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
- "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
- "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4985,8 +4548,6 @@
},
"node_modules/eslint-plugin-react": {
"version": "7.37.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
- "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5018,8 +4579,6 @@
},
"node_modules/eslint-plugin-react/node_modules/doctrine": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -5031,8 +4590,6 @@
},
"node_modules/eslint-plugin-react/node_modules/semver": {
"version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -5041,8 +4598,6 @@
},
"node_modules/eslint-scope": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
- "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -5055,8 +4610,6 @@
},
"node_modules/eslint-scope/node_modules/estraverse": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -5065,8 +4618,6 @@
},
"node_modules/eslint-visitor-keys": {
"version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -5078,8 +4629,6 @@
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -5095,8 +4644,6 @@
},
"node_modules/espree": {
"version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -5112,9 +4659,7 @@
}
},
"node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "version": "1.7.0",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -5126,8 +4671,6 @@
},
"node_modules/esrecurse": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -5139,8 +4682,6 @@
},
"node_modules/estraverse": {
"version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -5149,8 +4690,6 @@
},
"node_modules/estree-util-is-identifier-name": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
- "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -5159,8 +4698,6 @@
},
"node_modules/estree-walker": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5169,8 +4706,6 @@
},
"node_modules/esutils": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -5178,9 +4713,7 @@
}
},
"node_modules/expect-type": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
- "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
+ "version": "1.3.0",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -5189,21 +4722,15 @@
},
"node_modules/extend": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
"version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5219,8 +4746,6 @@
},
"node_modules/fast-glob/node_modules/glob-parent": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -5232,39 +4757,45 @@
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true,
"license": "MIT"
},
"node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "version": "1.20.1",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
}
},
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fflate": {
"version": "0.8.2",
- "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
- "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"dev": true,
"license": "MIT"
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5276,8 +4807,6 @@
},
"node_modules/fill-range": {
"version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5289,8 +4818,6 @@
},
"node_modules/find-up": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5306,8 +4833,6 @@
},
"node_modules/flat-cache": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5320,36 +4845,12 @@
}
},
"node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "version": "3.4.2",
"dev": true,
"license": "ISC"
},
- "node_modules/follow-redirects": {
- "version": "1.15.11",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
- "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
"node_modules/for-each": {
"version": "0.3.5",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
- "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5362,26 +4863,8 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/form-data": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
- "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
- "license": "MIT",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "es-set-tostringtag": "^2.1.0",
- "hasown": "^2.0.2",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/fs.realpath": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"license": "ISC"
},
@@ -5402,8 +4885,7 @@
},
"node_modules/function-bind": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -5411,8 +4893,6 @@
},
"node_modules/function.prototype.name": {
"version": "1.1.8",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
- "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5432,18 +4912,22 @@
},
"node_modules/functions-have-names": {
"version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5452,8 +4936,7 @@
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -5476,8 +4959,7 @@
},
"node_modules/get-proto": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -5489,8 +4971,6 @@
},
"node_modules/get-symbol-description": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
- "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5507,9 +4987,6 @@
},
"node_modules/glob": {
"version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -5529,8 +5006,6 @@
},
"node_modules/glob-parent": {
"version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -5542,8 +5017,6 @@
},
"node_modules/globals": {
"version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5558,8 +5031,6 @@
},
"node_modules/globalthis": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
- "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5575,8 +5046,6 @@
},
"node_modules/globby": {
"version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5596,8 +5065,7 @@
},
"node_modules/gopd": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5608,15 +5076,11 @@
},
"node_modules/graphemer": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true,
"license": "MIT"
},
"node_modules/has-bigints": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
- "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5628,8 +5092,6 @@
},
"node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"license": "MIT",
"engines": {
"node": ">=8"
@@ -5637,8 +5099,6 @@
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5650,8 +5110,6 @@
},
"node_modules/has-proto": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
- "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5666,8 +5124,7 @@
},
"node_modules/has-symbols": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5678,8 +5135,7 @@
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -5693,8 +5149,7 @@
},
"node_modules/hasown": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -5705,8 +5160,6 @@
},
"node_modules/hast-util-from-html": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
- "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -5723,8 +5176,6 @@
},
"node_modules/hast-util-from-parse5": {
"version": "8.0.3",
- "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
- "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -5743,8 +5194,6 @@
},
"node_modules/hast-util-from-parse5/node_modules/hastscript": {
"version": "9.0.1",
- "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
- "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -5760,8 +5209,6 @@
},
"node_modules/hast-util-parse-selector": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
- "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0"
@@ -5773,8 +5220,6 @@
},
"node_modules/hast-util-to-jsx-runtime": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
- "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
@@ -5800,8 +5245,6 @@
},
"node_modules/hast-util-whitespace": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
- "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0"
@@ -5813,8 +5256,6 @@
},
"node_modules/hastscript": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz",
- "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -5830,8 +5271,6 @@
},
"node_modules/hastscript/node_modules/property-information": {
"version": "6.5.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
- "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
"license": "MIT",
"funding": {
"type": "github",
@@ -5840,8 +5279,6 @@
},
"node_modules/html-encoding-sniffer": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
- "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5853,8 +5290,6 @@
},
"node_modules/html-url-attributes": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
- "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -5863,8 +5298,6 @@
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5877,8 +5310,6 @@
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
- "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5891,8 +5322,6 @@
},
"node_modules/iconv-lite": {
"version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5904,18 +5333,22 @@
},
"node_modules/ignore": {
"version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
+ "node_modules/immer": {
+ "version": "11.1.4",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-fresh": {
"version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5931,8 +5364,6 @@
},
"node_modules/imurmurhash": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5941,8 +5372,6 @@
},
"node_modules/indent-string": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"license": "MIT",
"engines": {
"node": ">=8"
@@ -5950,9 +5379,6 @@
},
"node_modules/inflight": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -5962,21 +5388,15 @@
},
"node_modules/inherits": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/inline-style-parser": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
- "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
+ "version": "0.2.7",
"license": "MIT"
},
"node_modules/internal-slot": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
- "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5990,8 +5410,6 @@
},
"node_modules/is-alphabetical": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
- "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -6000,8 +5418,6 @@
},
"node_modules/is-alphanumerical": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
- "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
"license": "MIT",
"dependencies": {
"is-alphabetical": "^2.0.0",
@@ -6014,8 +5430,6 @@
},
"node_modules/is-array-buffer": {
"version": "3.0.5",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
- "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6032,8 +5446,6 @@
},
"node_modules/is-async-function": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
- "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6052,8 +5464,6 @@
},
"node_modules/is-bigint": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
- "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6068,8 +5478,6 @@
},
"node_modules/is-boolean-object": {
"version": "1.2.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
- "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6085,8 +5493,6 @@
},
"node_modules/is-callable": {
"version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6098,8 +5504,6 @@
},
"node_modules/is-core-module": {
"version": "2.16.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
- "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6114,8 +5518,6 @@
},
"node_modules/is-data-view": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
- "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6132,8 +5534,6 @@
},
"node_modules/is-date-object": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
- "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6149,8 +5549,6 @@
},
"node_modules/is-decimal": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
- "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
"license": "MIT",
"funding": {
"type": "github",
@@ -6159,8 +5557,6 @@
},
"node_modules/is-extglob": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6169,8 +5565,6 @@
},
"node_modules/is-finalizationregistry": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
- "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6184,14 +5578,13 @@
}
},
"node_modules/is-generator-function": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
- "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "version": "1.1.2",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.0",
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
"has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0"
},
@@ -6204,8 +5597,6 @@
},
"node_modules/is-glob": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6217,8 +5608,6 @@
},
"node_modules/is-hexadecimal": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
- "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -6227,8 +5616,6 @@
},
"node_modules/is-map": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
- "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6240,8 +5627,6 @@
},
"node_modules/is-negative-zero": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
- "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6253,8 +5638,6 @@
},
"node_modules/is-number": {
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6263,8 +5646,6 @@
},
"node_modules/is-number-object": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
- "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6280,8 +5661,6 @@
},
"node_modules/is-path-inside": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6290,8 +5669,6 @@
},
"node_modules/is-plain-obj": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
- "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
"license": "MIT",
"engines": {
"node": ">=12"
@@ -6302,15 +5679,11 @@
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true,
"license": "MIT"
},
"node_modules/is-regex": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
- "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6328,8 +5701,6 @@
},
"node_modules/is-set": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
- "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6341,8 +5712,6 @@
},
"node_modules/is-shared-array-buffer": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
- "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6357,8 +5726,6 @@
},
"node_modules/is-string": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
- "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6374,8 +5741,6 @@
},
"node_modules/is-symbol": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
- "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6392,8 +5757,6 @@
},
"node_modules/is-typed-array": {
"version": "1.1.15",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
- "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6408,8 +5771,6 @@
},
"node_modules/is-weakmap": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
- "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6421,8 +5782,6 @@
},
"node_modules/is-weakref": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
- "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6437,8 +5796,6 @@
},
"node_modules/is-weakset": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
- "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6454,22 +5811,16 @@
},
"node_modules/isarray": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true,
"license": "MIT"
},
"node_modules/isexe": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/iterator.prototype": {
"version": "1.1.5",
- "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
- "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6486,8 +5837,6 @@
},
"node_modules/jest-diff": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz",
- "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==",
"license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
@@ -6501,8 +5850,6 @@
},
"node_modules/jest-get-type": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz",
- "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==",
"license": "MIT",
"engines": {
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
@@ -6510,8 +5857,6 @@
},
"node_modules/jest-matcher-utils": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz",
- "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==",
"license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
@@ -6525,14 +5870,10 @@
},
"node_modules/js-tokens": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6544,8 +5885,6 @@
},
"node_modules/jsdom": {
"version": "26.1.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
- "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6584,8 +5923,6 @@
},
"node_modules/jsesc": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
"bin": {
@@ -6597,29 +5934,21 @@
},
"node_modules/json-buffer": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -6631,8 +5960,6 @@
},
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
- "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6647,14 +5974,10 @@
},
"node_modules/keyborg": {
"version": "2.6.0",
- "resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.6.0.tgz",
- "integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==",
"license": "MIT"
},
"node_modules/keyv": {
"version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6663,8 +5986,6 @@
},
"node_modules/levn": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6677,8 +5998,6 @@
},
"node_modules/locate-path": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6693,15 +6012,11 @@
},
"node_modules/lodash.merge": {
"version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/longest-streak": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
- "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
"license": "MIT",
"funding": {
"type": "github",
@@ -6710,8 +6025,6 @@
},
"node_modules/loose-envify": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -6722,15 +6035,11 @@
},
"node_modules/loupe": {
"version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
- "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
"dev": true,
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -6739,17 +6048,13 @@
},
"node_modules/lz-string": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
- "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"license": "MIT",
"bin": {
"lz-string": "bin/bin.js"
}
},
"node_modules/magic-string": {
- "version": "0.30.18",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
- "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
+ "version": "0.30.21",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6758,8 +6063,6 @@
},
"node_modules/markdown-table": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
- "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
"license": "MIT",
"funding": {
"type": "github",
@@ -6768,8 +6071,7 @@
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -6777,8 +6079,6 @@
},
"node_modules/mdast-util-find-and-replace": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
- "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6793,8 +6093,6 @@
},
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
- "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"license": "MIT",
"engines": {
"node": ">=12"
@@ -6804,9 +6102,7 @@
}
},
"node_modules/mdast-util-from-markdown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
- "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "version": "2.0.3",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6829,8 +6125,6 @@
},
"node_modules/mdast-util-gfm": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
- "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
"license": "MIT",
"dependencies": {
"mdast-util-from-markdown": "^2.0.0",
@@ -6848,8 +6142,6 @@
},
"node_modules/mdast-util-gfm-autolink-literal": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
- "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6865,8 +6157,6 @@
},
"node_modules/mdast-util-gfm-footnote": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
- "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6882,8 +6172,6 @@
},
"node_modules/mdast-util-gfm-strikethrough": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
- "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6897,8 +6185,6 @@
},
"node_modules/mdast-util-gfm-table": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
- "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6914,8 +6200,6 @@
},
"node_modules/mdast-util-gfm-task-list-item": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
- "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -6930,8 +6214,6 @@
},
"node_modules/mdast-util-mdx-expression": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
- "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
"license": "MIT",
"dependencies": {
"@types/estree-jsx": "^1.0.0",
@@ -6948,8 +6230,6 @@
},
"node_modules/mdast-util-mdx-jsx": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
- "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
"license": "MIT",
"dependencies": {
"@types/estree-jsx": "^1.0.0",
@@ -6972,8 +6252,6 @@
},
"node_modules/mdast-util-mdxjs-esm": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
- "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
"license": "MIT",
"dependencies": {
"@types/estree-jsx": "^1.0.0",
@@ -6990,8 +6268,6 @@
},
"node_modules/mdast-util-phrasing": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
- "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -7003,9 +6279,7 @@
}
},
"node_modules/mdast-util-to-hast": {
- "version": "13.2.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
- "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "version": "13.2.1",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -7025,8 +6299,6 @@
},
"node_modules/mdast-util-to-markdown": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
- "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -7046,8 +6318,6 @@
},
"node_modules/mdast-util-to-string": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
- "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0"
@@ -7059,8 +6329,6 @@
},
"node_modules/merge2": {
"version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7069,8 +6337,6 @@
},
"node_modules/micromark": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
- "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7104,8 +6370,6 @@
},
"node_modules/micromark-core-commonmark": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
- "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7138,8 +6402,6 @@
},
"node_modules/micromark-extension-gfm": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
- "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
"license": "MIT",
"dependencies": {
"micromark-extension-gfm-autolink-literal": "^2.0.0",
@@ -7158,8 +6420,6 @@
},
"node_modules/micromark-extension-gfm-autolink-literal": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
- "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
"license": "MIT",
"dependencies": {
"micromark-util-character": "^2.0.0",
@@ -7174,8 +6434,6 @@
},
"node_modules/micromark-extension-gfm-footnote": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
- "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
@@ -7194,8 +6452,6 @@
},
"node_modules/micromark-extension-gfm-strikethrough": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
- "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
@@ -7212,8 +6468,6 @@
},
"node_modules/micromark-extension-gfm-table": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
- "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
@@ -7229,8 +6483,6 @@
},
"node_modules/micromark-extension-gfm-tagfilter": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
- "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
"license": "MIT",
"dependencies": {
"micromark-util-types": "^2.0.0"
@@ -7242,8 +6494,6 @@
},
"node_modules/micromark-extension-gfm-task-list-item": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
- "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
@@ -7259,8 +6509,6 @@
},
"node_modules/micromark-factory-destination": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
- "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7280,8 +6528,6 @@
},
"node_modules/micromark-factory-label": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
- "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7302,8 +6548,6 @@
},
"node_modules/micromark-factory-space": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
- "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7322,8 +6566,6 @@
},
"node_modules/micromark-factory-title": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
- "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7344,8 +6586,6 @@
},
"node_modules/micromark-factory-whitespace": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
- "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7366,8 +6606,6 @@
},
"node_modules/micromark-util-character": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
- "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7386,8 +6624,6 @@
},
"node_modules/micromark-util-chunked": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
- "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7405,8 +6641,6 @@
},
"node_modules/micromark-util-classify-character": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
- "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7426,8 +6660,6 @@
},
"node_modules/micromark-util-combine-extensions": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
- "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7446,8 +6678,6 @@
},
"node_modules/micromark-util-decode-numeric-character-reference": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
- "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7465,8 +6695,6 @@
},
"node_modules/micromark-util-decode-string": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
- "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7487,8 +6715,6 @@
},
"node_modules/micromark-util-encode": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
- "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7503,8 +6729,6 @@
},
"node_modules/micromark-util-html-tag-name": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
- "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7519,8 +6743,6 @@
},
"node_modules/micromark-util-normalize-identifier": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
- "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7538,8 +6760,6 @@
},
"node_modules/micromark-util-resolve-all": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
- "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7557,8 +6777,6 @@
},
"node_modules/micromark-util-sanitize-uri": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
- "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7578,8 +6796,6 @@
},
"node_modules/micromark-util-subtokenize": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
- "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7600,8 +6816,6 @@
},
"node_modules/micromark-util-symbol": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
- "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7616,8 +6830,6 @@
},
"node_modules/micromark-util-types": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
- "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -7632,8 +6844,6 @@
},
"node_modules/micromatch": {
"version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7644,40 +6854,15 @@
"node": ">=8.6"
}
},
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/min-indent": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
- "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "version": "3.1.3",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -7689,8 +6874,6 @@
},
"node_modules/mrmime": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
- "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7699,14 +6882,10 @@
},
"node_modules/ms": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@@ -7724,29 +6903,46 @@
},
"node_modules/natural-compare": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true,
"license": "MIT"
},
"node_modules/natural-compare-lite": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
- "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true,
"license": "MIT"
},
+ "node_modules/node-exports-info": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array.prototype.flatmap": "^1.3.3",
+ "es-errors": "^1.3.0",
+ "object.entries": "^1.1.9",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/node-exports-info/node_modules/semver": {
+ "version": "6.3.1",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/node-releases": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.20.tgz",
- "integrity": "sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==",
+ "version": "2.0.37",
"dev": true,
"license": "MIT"
},
"node_modules/nth-check": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
@@ -7756,16 +6952,13 @@
}
},
"node_modules/nwsapi": {
- "version": "2.2.22",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz",
- "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==",
+ "version": "2.2.23",
"dev": true,
"license": "MIT"
},
"node_modules/object-assign": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -7773,8 +6966,6 @@
},
"node_modules/object-inspect": {
"version": "1.13.4",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7786,8 +6977,6 @@
},
"node_modules/object-keys": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7796,8 +6985,6 @@
},
"node_modules/object.assign": {
"version": "4.1.7",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
- "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7817,8 +7004,6 @@
},
"node_modules/object.entries": {
"version": "1.1.9",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
- "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7833,8 +7018,6 @@
},
"node_modules/object.fromentries": {
"version": "2.0.8",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
- "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7852,8 +7035,6 @@
},
"node_modules/object.values": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
- "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7871,8 +7052,6 @@
},
"node_modules/once": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -7881,8 +7060,6 @@
},
"node_modules/optionator": {
"version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7899,8 +7076,6 @@
},
"node_modules/own-keys": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
- "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7917,8 +7092,6 @@
},
"node_modules/p-limit": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7933,8 +7106,6 @@
},
"node_modules/p-locate": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7949,8 +7120,6 @@
},
"node_modules/parent-module": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7962,8 +7131,6 @@
},
"node_modules/parse-entities": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
- "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
"license": "MIT",
"dependencies": {
"@types/unist": "^2.0.0",
@@ -7981,14 +7148,10 @@
},
"node_modules/parse-entities/node_modules/@types/unist": {
"version": "2.0.11",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
- "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
"license": "MIT"
},
"node_modules/parse5": {
"version": "7.3.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
- "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
@@ -7999,8 +7162,6 @@
},
"node_modules/path-exists": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8009,8 +7170,6 @@
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8019,8 +7178,6 @@
},
"node_modules/path-key": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8029,15 +7186,11 @@
},
"node_modules/path-parse": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/path-type": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8046,15 +7199,11 @@
},
"node_modules/pathe": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/pathval": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
- "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8063,18 +7212,14 @@
},
"node_modules/picocolors": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "4.0.4",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
@@ -8082,8 +7227,6 @@
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
- "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8091,9 +7234,7 @@
}
},
"node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "version": "8.5.9",
"dev": true,
"funding": [
{
@@ -8121,8 +7262,6 @@
},
"node_modules/prelude-ls": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8131,8 +7270,6 @@
},
"node_modules/pretty-format": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
- "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1",
@@ -8145,8 +7282,6 @@
},
"node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"license": "MIT",
"engines": {
"node": ">=10"
@@ -8157,8 +7292,6 @@
},
"node_modules/prismjs": {
"version": "1.30.0",
- "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
- "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8166,8 +7299,7 @@
},
"node_modules/prop-types": {
"version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -8177,30 +7309,19 @@
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/property-information": {
"version": "7.1.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
- "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
- "license": "MIT"
- },
"node_modules/punycode": {
"version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8209,8 +7330,6 @@
},
"node_modules/queue-microtask": {
"version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
@@ -8230,8 +7349,6 @@
},
"node_modules/react": {
"version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
- "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -8242,8 +7359,6 @@
},
"node_modules/react-dom": {
"version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
- "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
@@ -8255,8 +7370,6 @@
},
"node_modules/react-dom/node_modules/scheduler": {
"version": "0.23.2",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
- "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -8264,14 +7377,10 @@
},
"node_modules/react-is": {
"version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"license": "MIT"
},
"node_modules/react-markdown": {
"version": "10.1.0",
- "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
- "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -8295,10 +7404,29 @@
"react": ">=18"
}
},
+ "node_modules/react-redux": {
+ "version": "9.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.6",
+ "use-sync-external-store": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25 || ^19",
+ "react": "^18.0 || ^19",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-refresh": {
"version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8306,9 +7434,7 @@
}
},
"node_modules/react-router": {
- "version": "7.8.2",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz",
- "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==",
+ "version": "7.14.0",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -8328,12 +7454,10 @@
}
},
"node_modules/react-router-dom": {
- "version": "7.8.2",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.2.tgz",
- "integrity": "sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==",
+ "version": "7.14.0",
"license": "MIT",
"dependencies": {
- "react-router": "7.8.2"
+ "react-router": "7.14.0"
},
"engines": {
"node": ">=20.0.0"
@@ -8343,26 +7467,8 @@
"react-dom": ">=18"
}
},
- "node_modules/react-transition-group": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@babel/runtime": "^7.5.5",
- "dom-helpers": "^5.0.1",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.6.2"
- },
- "peerDependencies": {
- "react": ">=16.6.0",
- "react-dom": ">=16.6.0"
- }
- },
"node_modules/redent": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
- "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
"license": "MIT",
"dependencies": {
"indent-string": "^4.0.0",
@@ -8372,10 +7478,19 @@
"node": ">=8"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "license": "MIT"
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "license": "MIT",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
- "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
- "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8397,8 +7512,6 @@
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
- "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8418,8 +7531,6 @@
},
"node_modules/rehype-parse": {
"version": "9.0.1",
- "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
- "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -8433,8 +7544,6 @@
},
"node_modules/rehype-prism": {
"version": "2.3.3",
- "resolved": "https://registry.npmjs.org/rehype-prism/-/rehype-prism-2.3.3.tgz",
- "integrity": "sha512-J9mhio/CwcJRDyIhsp5hgXmyGeQsFN+/1eNEKnBRxfdJAx2CqH41kV0dqn/k2OgMdjk21IoGFgar0MfVtGYTSg==",
"license": "MIT",
"dependencies": {
"hastscript": "^8.0.0",
@@ -8450,8 +7559,6 @@
},
"node_modules/remark-gfm": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
- "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -8468,8 +7575,6 @@
},
"node_modules/remark-parse": {
"version": "11.0.0",
- "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
- "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -8484,8 +7589,6 @@
},
"node_modules/remark-rehype": {
"version": "11.1.2",
- "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
- "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -8501,8 +7604,6 @@
},
"node_modules/remark-stringify": {
"version": "11.0.0",
- "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
- "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -8514,28 +7615,34 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "license": "MIT"
+ },
"node_modules/resolve": {
- "version": "2.0.0-next.5",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
- "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "version": "2.0.0-next.6",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.13.0",
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "node-exports-info": "^1.6.0",
+ "object-keys": "^1.1.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8544,8 +7651,6 @@
},
"node_modules/reusify": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8555,9 +7660,6 @@
},
"node_modules/rimraf": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -8571,9 +7673,7 @@
}
},
"node_modules/rollup": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz",
- "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==",
+ "version": "4.60.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8587,55 +7687,41 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.50.1",
- "@rollup/rollup-android-arm64": "4.50.1",
- "@rollup/rollup-darwin-arm64": "4.50.1",
- "@rollup/rollup-darwin-x64": "4.50.1",
- "@rollup/rollup-freebsd-arm64": "4.50.1",
- "@rollup/rollup-freebsd-x64": "4.50.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.50.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.50.1",
- "@rollup/rollup-linux-arm64-gnu": "4.50.1",
- "@rollup/rollup-linux-arm64-musl": "4.50.1",
- "@rollup/rollup-linux-loongarch64-gnu": "4.50.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.50.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.50.1",
- "@rollup/rollup-linux-riscv64-musl": "4.50.1",
- "@rollup/rollup-linux-s390x-gnu": "4.50.1",
- "@rollup/rollup-linux-x64-gnu": "4.50.1",
- "@rollup/rollup-linux-x64-musl": "4.50.1",
- "@rollup/rollup-openharmony-arm64": "4.50.1",
- "@rollup/rollup-win32-arm64-msvc": "4.50.1",
- "@rollup/rollup-win32-ia32-msvc": "4.50.1",
- "@rollup/rollup-win32-x64-msvc": "4.50.1",
+ "@rollup/rollup-android-arm-eabi": "4.60.1",
+ "@rollup/rollup-android-arm64": "4.60.1",
+ "@rollup/rollup-darwin-arm64": "4.60.1",
+ "@rollup/rollup-darwin-x64": "4.60.1",
+ "@rollup/rollup-freebsd-arm64": "4.60.1",
+ "@rollup/rollup-freebsd-x64": "4.60.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.60.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.60.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.60.1",
+ "@rollup/rollup-linux-arm64-musl": "4.60.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.60.1",
+ "@rollup/rollup-linux-loong64-musl": "4.60.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.60.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.60.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.60.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.60.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.60.1",
+ "@rollup/rollup-linux-x64-gnu": "4.60.1",
+ "@rollup/rollup-linux-x64-musl": "4.60.1",
+ "@rollup/rollup-openbsd-x64": "4.60.1",
+ "@rollup/rollup-openharmony-arm64": "4.60.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.60.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.60.1",
+ "@rollup/rollup-win32-x64-gnu": "4.60.1",
+ "@rollup/rollup-win32-x64-msvc": "4.60.1",
"fsevents": "~2.3.2"
}
},
- "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.50.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz",
- "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
"node_modules/rrweb-cssom": {
"version": "0.8.0",
- "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
- "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
"dev": true,
"license": "MIT"
},
"node_modules/rtl-css-js": {
"version": "1.16.1",
- "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
- "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2"
@@ -8643,8 +7729,6 @@
},
"node_modules/run-parallel": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
@@ -8667,8 +7751,6 @@
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
- "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8687,8 +7769,6 @@
},
"node_modules/safe-push-apply": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
- "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8704,8 +7784,6 @@
},
"node_modules/safe-regex-test": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
- "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8722,15 +7800,11 @@
},
"node_modules/safer-buffer": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/saxes": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
- "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -8741,19 +7815,12 @@
}
},
"node_modules/scheduler": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
- "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "version": "0.27.0",
"license": "MIT",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0"
- }
+ "peer": true
},
"node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.4",
"dev": true,
"license": "ISC",
"bin": {
@@ -8764,15 +7831,11 @@
}
},
"node_modules/set-cookie-parser": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
- "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
+ "version": "2.7.2",
"license": "MIT"
},
"node_modules/set-function-length": {
"version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8789,8 +7852,6 @@
},
"node_modules/set-function-name": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
- "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8805,8 +7866,6 @@
},
"node_modules/set-proto": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
- "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8820,8 +7879,6 @@
},
"node_modules/shebang-command": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8833,8 +7890,6 @@
},
"node_modules/shebang-regex": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8843,8 +7898,6 @@
},
"node_modules/side-channel": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8863,8 +7916,6 @@
},
"node_modules/side-channel-list": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8880,8 +7931,6 @@
},
"node_modules/side-channel-map": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8899,8 +7948,6 @@
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8919,15 +7966,11 @@
},
"node_modules/siginfo": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
- "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
"dev": true,
"license": "ISC"
},
"node_modules/sirv": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
- "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8941,8 +7984,6 @@
},
"node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8951,8 +7992,6 @@
},
"node_modules/source-map-js": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -8961,8 +8000,6 @@
},
"node_modules/space-separated-tokens": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
- "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
"license": "MIT",
"funding": {
"type": "github",
@@ -8971,22 +8008,16 @@
},
"node_modules/stackback": {
"version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
- "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
"dev": true,
"license": "MIT"
},
"node_modules/std-env": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
- "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
+ "version": "3.10.0",
"dev": true,
"license": "MIT"
},
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
- "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8999,8 +8030,6 @@
},
"node_modules/string.prototype.matchall": {
"version": "4.0.12",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
- "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9027,8 +8056,6 @@
},
"node_modules/string.prototype.repeat": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
- "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9038,8 +8065,6 @@
},
"node_modules/string.prototype.trim": {
"version": "1.2.10",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
- "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9060,8 +8085,6 @@
},
"node_modules/string.prototype.trimend": {
"version": "1.0.9",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
- "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9079,8 +8102,6 @@
},
"node_modules/string.prototype.trimstart": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
- "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9097,8 +8118,6 @@
},
"node_modules/stringify-entities": {
"version": "4.0.4",
- "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
- "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
"license": "MIT",
"dependencies": {
"character-entities-html4": "^2.0.0",
@@ -9111,8 +8130,6 @@
},
"node_modules/strip-ansi": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9124,8 +8141,6 @@
},
"node_modules/strip-indent": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
- "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"license": "MIT",
"dependencies": {
"min-indent": "^1.0.0"
@@ -9136,8 +8151,6 @@
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"license": "MIT",
"engines": {
@@ -9148,9 +8161,7 @@
}
},
"node_modules/strip-literal": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz",
- "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
+ "version": "3.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9162,39 +8173,29 @@
},
"node_modules/strip-literal/node_modules/js-tokens": {
"version": "9.0.1",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
- "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"dev": true,
"license": "MIT"
},
"node_modules/style-to-js": {
- "version": "1.1.17",
- "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
- "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
+ "version": "1.1.21",
"license": "MIT",
"dependencies": {
- "style-to-object": "1.0.9"
+ "style-to-object": "1.0.14"
}
},
"node_modules/style-to-object": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
- "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
+ "version": "1.0.14",
"license": "MIT",
"dependencies": {
- "inline-style-parser": "0.2.4"
+ "inline-style-parser": "0.2.7"
}
},
"node_modules/stylis": {
"version": "4.3.6",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
- "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
@@ -9205,8 +8206,6 @@
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -9218,54 +8217,55 @@
},
"node_modules/symbol-tree": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true,
"license": "MIT"
},
"node_modules/tabster": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/tabster/-/tabster-8.5.6.tgz",
- "integrity": "sha512-2vfrRGrx8O9BjdrtSlVA5fvpmbq5HQBRN13XFRg6LAvZ1Fr3QdBnswgT4YgFS5Bhoo5nxwgjRaRueI2Us/dv7g==",
+ "version": "8.7.0",
"license": "MIT",
"dependencies": {
"keyborg": "2.6.0",
"tslib": "^2.8.1"
},
"optionalDependencies": {
- "@rollup/rollup-linux-x64-gnu": "4.40.0"
+ "@rollup/rollup-linux-x64-gnu": "4.53.3"
}
},
+ "node_modules/tabster/node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
"node_modules/text-table": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true,
"license": "MIT"
},
"node_modules/tinybench": {
"version": "2.9.0",
- "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
- "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
- "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "version": "0.2.16",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
- "picomatch": "^4.0.3"
+ "picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
@@ -9274,41 +8274,8 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/tinyglobby/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/tinypool": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
- "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -9317,8 +8284,6 @@
},
"node_modules/tinyrainbow": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
- "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -9326,9 +8291,7 @@
}
},
"node_modules/tinyspy": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz",
- "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==",
+ "version": "4.0.4",
"dev": true,
"license": "MIT",
"engines": {
@@ -9337,8 +8300,6 @@
},
"node_modules/tldts": {
"version": "6.1.86",
- "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
- "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9350,15 +8311,11 @@
},
"node_modules/tldts-core": {
"version": "6.1.86",
- "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
- "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
"dev": true,
"license": "MIT"
},
"node_modules/to-regex-range": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9370,8 +8327,6 @@
},
"node_modules/totalist": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
- "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -9380,8 +8335,6 @@
},
"node_modules/tough-cookie": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
- "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -9393,8 +8346,6 @@
},
"node_modules/tr46": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
- "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9406,8 +8357,6 @@
},
"node_modules/trim-lines": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
- "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -9416,8 +8365,6 @@
},
"node_modules/trough": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
- "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
"license": "MIT",
"funding": {
"type": "github",
@@ -9426,14 +8373,10 @@
},
"node_modules/tslib": {
"version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/tsutils": {
"version": "3.21.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
- "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9448,15 +8391,11 @@
},
"node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true,
"license": "0BSD"
},
"node_modules/type-check": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9468,8 +8407,6 @@
},
"node_modules/type-fest": {
"version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
@@ -9481,8 +8418,6 @@
},
"node_modules/typed-array-buffer": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
- "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9496,8 +8431,6 @@
},
"node_modules/typed-array-byte-length": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
- "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9516,8 +8449,6 @@
},
"node_modules/typed-array-byte-offset": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
- "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9538,8 +8469,6 @@
},
"node_modules/typed-array-length": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
- "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9558,9 +8487,7 @@
}
},
"node_modules/typescript": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "version": "5.9.3",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -9573,8 +8500,6 @@
},
"node_modules/unbox-primitive": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
- "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9592,15 +8517,11 @@
},
"node_modules/undici-types": {
"version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/unified": {
"version": "11.0.5",
- "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
- "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9617,9 +8538,7 @@
}
},
"node_modules/unist-util-is": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
- "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "version": "6.0.1",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
@@ -9631,8 +8550,6 @@
},
"node_modules/unist-util-position": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
- "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
@@ -9644,8 +8561,6 @@
},
"node_modules/unist-util-select": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/unist-util-select/-/unist-util-select-5.1.0.tgz",
- "integrity": "sha512-4A5mfokSHG/rNQ4g7gSbdEs+H586xyd24sdJqF1IWamqrLHvYb+DH48fzxowyOhOfK7YSqX+XlCojAyuuyyT2A==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9661,8 +8576,6 @@
},
"node_modules/unist-util-stringify-position": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
- "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
@@ -9673,9 +8586,7 @@
}
},
"node_modules/unist-util-visit": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
- "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "version": "5.1.0",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9688,9 +8599,7 @@
}
},
"node_modules/unist-util-visit-parents": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
- "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "version": "6.0.2",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9702,9 +8611,7 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "version": "1.2.3",
"dev": true,
"funding": [
{
@@ -9734,8 +8641,6 @@
},
"node_modules/uri-js": {
"version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -9743,9 +8648,7 @@
}
},
"node_modules/use-sync-external-store": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
- "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+ "version": "1.6.0",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -9753,8 +8656,6 @@
},
"node_modules/vfile": {
"version": "6.0.3",
- "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
- "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9767,8 +8668,6 @@
},
"node_modules/vfile-location": {
"version": "5.0.3",
- "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
- "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9781,8 +8680,6 @@
},
"node_modules/vfile-message": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
- "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -9794,13 +8691,13 @@
}
},
"node_modules/vite": {
- "version": "7.1.5",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
- "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
+ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.25.0",
+ "esbuild": "^0.27.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
@@ -9870,8 +8767,6 @@
},
"node_modules/vite-node": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
- "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9891,41 +8786,8 @@
"url": "https://opencollective.com/vitest"
}
},
- "node_modules/vite/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/vite/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/vitest": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
- "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9995,23 +8857,8 @@
}
}
},
- "node_modules/vitest/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
- "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10023,8 +8870,6 @@
},
"node_modules/web-namespaces": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
- "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -10033,14 +8878,10 @@
},
"node_modules/web-vitals": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz",
- "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==",
"license": "Apache-2.0"
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -10049,8 +8890,6 @@
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
- "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10062,8 +8901,6 @@
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
- "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10072,8 +8909,6 @@
},
"node_modules/whatwg-url": {
"version": "14.2.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
- "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10086,8 +8921,6 @@
},
"node_modules/which": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -10102,8 +8935,6 @@
},
"node_modules/which-boxed-primitive": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
- "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10122,8 +8953,6 @@
},
"node_modules/which-builtin-type": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
- "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10150,8 +8979,6 @@
},
"node_modules/which-collection": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
- "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10168,9 +8995,7 @@
}
},
"node_modules/which-typed-array": {
- "version": "1.1.19",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
- "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "version": "1.1.20",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10191,8 +9016,6 @@
},
"node_modules/why-is-node-running": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
- "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10208,8 +9031,6 @@
},
"node_modules/word-wrap": {
"version": "1.2.5",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10218,15 +9039,11 @@
},
"node_modules/wrappy": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
},
"node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "version": "8.20.0",
"dev": true,
"license": "MIT",
"engines": {
@@ -10247,8 +9064,6 @@
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
- "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -10257,22 +9072,16 @@
},
"node_modules/xmlchars": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true,
"license": "MIT"
},
"node_modules/yallist": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"license": "ISC"
},
"node_modules/yocto-queue": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10284,8 +9093,6 @@
},
"node_modules/zwitch": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
- "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
"license": "MIT",
"funding": {
"type": "github",
diff --git a/src/frontend/package.json b/src/App/package.json
similarity index 84%
rename from src/frontend/package.json
rename to src/App/package.json
index fabef9d44..454a219f5 100644
--- a/src/frontend/package.json
+++ b/src/App/package.json
@@ -7,6 +7,7 @@
"@fluentui/merge-styles": "^8.6.14",
"@fluentui/react-components": "^9.64.0",
"@fluentui/react-icons": "^2.0.300",
+ "@reduxjs/toolkit": "^2.11.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
@@ -15,11 +16,11 @@
"@types/node": "^16.18.126",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
- "axios": "^1.11.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^10.1.0",
- "react-router-dom": "^7.6.0",
+ "react-redux": "^9.2.0",
+ "react-router-dom": "^7.12.0",
"rehype-prism": "^2.3.3",
"remark-gfm": "^4.0.1",
"web-vitals": "^2.1.4"
@@ -63,9 +64,18 @@
"@vitest/ui": "^3.2.4",
"eslint": "^8.57.1",
"eslint-plugin-react": "^7.37.5",
+ "flatted": "^3.4.2",
"jsdom": "^26.1.0",
+ "rollup": "^4.59.0",
"typescript": "^5.8.3",
- "vite": "^7.1.2",
+ "vite": "7.3.2",
"vitest": "^3.2.4"
+ },
+ "overrides": {
+ "minimatch": "3.1.3",
+ "vite": "7.3.2",
+ "mdast-util-to-hast": "13.2.1",
+ "picomatch": "4.0.4",
+ "js-yaml": "4.1.1"
}
-}
\ No newline at end of file
+}
diff --git a/src/frontend/public/contosoLogo.svg b/src/App/public/contosoLogo.svg
similarity index 100%
rename from src/frontend/public/contosoLogo.svg
rename to src/App/public/contosoLogo.svg
diff --git a/src/frontend/public/favicon-96x96.png b/src/App/public/favicon-96x96.png
similarity index 100%
rename from src/frontend/public/favicon-96x96.png
rename to src/App/public/favicon-96x96.png
diff --git a/src/frontend/public/favicon.ico b/src/App/public/favicon.ico
similarity index 100%
rename from src/frontend/public/favicon.ico
rename to src/App/public/favicon.ico
diff --git a/src/frontend/public/index.html b/src/App/public/index.html
similarity index 100%
rename from src/frontend/public/index.html
rename to src/App/public/index.html
diff --git a/src/frontend/public/logo192.png b/src/App/public/logo192.png
similarity index 100%
rename from src/frontend/public/logo192.png
rename to src/App/public/logo192.png
diff --git a/src/frontend/public/logo512.png b/src/App/public/logo512.png
similarity index 100%
rename from src/frontend/public/logo512.png
rename to src/App/public/logo512.png
diff --git a/src/frontend/public/manifest.json b/src/App/public/manifest.json
similarity index 100%
rename from src/frontend/public/manifest.json
rename to src/App/public/manifest.json
diff --git a/src/frontend/public/robots.txt b/src/App/public/robots.txt
similarity index 100%
rename from src/frontend/public/robots.txt
rename to src/App/public/robots.txt
diff --git a/src/App/pyproject.toml b/src/App/pyproject.toml
new file mode 100644
index 000000000..3b19931ba
--- /dev/null
+++ b/src/App/pyproject.toml
@@ -0,0 +1,14 @@
+[project]
+name = "frontend-react"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.11"
+dependencies = [
+ "fastapi",
+ "uvicorn[standard]>=0.38.0",
+ "jinja2",
+ "azure-identity",
+ "python-dotenv",
+ "python-multipart",
+]
diff --git a/src/frontend/requirements.txt b/src/App/requirements.txt
similarity index 100%
rename from src/frontend/requirements.txt
rename to src/App/requirements.txt
diff --git a/src/frontend/src/App.css b/src/App/src/App.css
similarity index 100%
rename from src/frontend/src/App.css
rename to src/App/src/App.css
diff --git a/src/frontend/src/App.tsx b/src/App/src/App.tsx
similarity index 89%
rename from src/frontend/src/App.tsx
rename to src/App/src/App.tsx
index 79d54ede3..a4fc17c6d 100644
--- a/src/frontend/src/App.tsx
+++ b/src/App/src/App.tsx
@@ -5,7 +5,7 @@ import { HomePage, PlanPage } from './pages';
import { useWebSocket } from './hooks/useWebSocket';
function App() {
- const { isConnected, isConnecting, error } = useWebSocket();
+ useWebSocket();
return (
diff --git a/src/App/src/api/apiClient.tsx b/src/App/src/api/apiClient.tsx
new file mode 100644
index 000000000..7eaab10f2
--- /dev/null
+++ b/src/App/src/api/apiClient.tsx
@@ -0,0 +1,52 @@
+/**
+ * API Client โ thin adapter over the centralized httpClient.
+ *
+ * Auth headers (x-ms-client-principal-id, Authorization) are now injected
+ * automatically by httpClient's request interceptor, eliminating all manual
+ * headerBuilder() / localStorage.getItem('token') calls.
+ */
+import httpClient from './httpClient';
+import { getApiUrl } from './config';
+
+/**
+ * Ensure httpClient's base URL stays in sync with the runtime config.
+ * Called lazily on every request so it picks up late-initialized API_URL.
+ */
+function syncBaseUrl(): void {
+ const apiUrl = getApiUrl();
+ if (apiUrl && httpClient.getBaseUrl() !== apiUrl) {
+ httpClient.setBaseUrl(apiUrl);
+ }
+}
+
+export const apiClient = {
+ get: (url: string, config?: { params?: Record }): Promise => {
+ syncBaseUrl();
+ return httpClient.get(url, { params: config?.params });
+ },
+
+ post: (url: string, body?: unknown): Promise => {
+ syncBaseUrl();
+ return httpClient.post(url, body);
+ },
+
+ put: (url: string, body?: unknown): Promise => {
+ syncBaseUrl();
+ return httpClient.put(url, body);
+ },
+
+ delete: (url: string): Promise => {
+ syncBaseUrl();
+ return httpClient.del(url);
+ },
+
+ upload: (url: string, formData: FormData): Promise => {
+ syncBaseUrl();
+ return httpClient.upload(url, formData);
+ },
+
+ login: (url: string, body?: unknown): Promise => {
+ syncBaseUrl();
+ return httpClient.postWithoutAuth(url, body);
+ },
+};
diff --git a/src/frontend/src/api/apiService.tsx b/src/App/src/api/apiService.tsx
similarity index 91%
rename from src/frontend/src/api/apiService.tsx
rename to src/App/src/api/apiService.tsx
index 8be89ec9f..f6f6ba3d9 100644
--- a/src/frontend/src/api/apiService.tsx
+++ b/src/App/src/api/apiService.tsx
@@ -5,12 +5,8 @@ import {
InputTask,
InputTaskResponse,
Plan,
- StepStatus,
- AgentType,
PlanApprovalRequest,
PlanApprovalResponse,
- AgentMessageData,
- MPlanData,
AgentMessageBE,
MPlanBE,
TeamConfigurationBE,
@@ -20,13 +16,13 @@ import {
// Constants for endpoints
const API_ENDPOINTS = {
- PROCESS_REQUEST: '/v3/process_request',
- PLANS: '/v3/plans',
- PLAN: '/v3/plan',
- PLAN_APPROVAL: '/v3/plan_approval',
- HUMAN_CLARIFICATION: '/v3/user_clarification',
+ PROCESS_REQUEST: '/v4/process_request',
+ PLANS: '/v4/plans',
+ PLAN: '/v4/plan',
+ PLAN_APPROVAL: '/v4/plan_approval',
+ HUMAN_CLARIFICATION: '/v4/user_clarification',
USER_BROWSER_LANGUAGE: '/user_browser_language',
- AGENT_MESSAGE: '/v3/agent_message',
+ AGENT_MESSAGE: '/v4/agent_message',
};
// Simple cache implementation
@@ -160,7 +156,6 @@ export class APIService {
if (!data) {
throw new Error(`Plan with ID ${planId} not found`);
}
- console.log('Fetched plan by ID:', data);
const results = {
plan: data.plan as Plan,
messages: data.messages as AgentMessageBE[],
@@ -194,8 +189,6 @@ export class APIService {
const requestKey = `approve-plan-${planApprovalData.m_plan_id}`;
return this._requestTracker.trackRequest(requestKey, async () => {
- console.log('๐ค Approving plan via v3 API:', planApprovalData);
-
const response = await apiClient.post(API_ENDPOINTS.PLAN_APPROVAL, planApprovalData);
// Invalidate cache since plan execution will start
@@ -204,7 +197,6 @@ export class APIService {
this._cache.invalidate(new RegExp(`^plan.*_${planApprovalData.plan_id}`));
}
- console.log('โ
Plan approval successful:', response);
return response;
});
}
@@ -264,13 +256,7 @@ export class APIService {
return response;
}
async sendAgentMessage(data: AgentMessageResponse): Promise {
- const t0 = performance.now();
const result = await apiClient.post(API_ENDPOINTS.AGENT_MESSAGE, data);
- console.log('[agent_message] sent', {
- ms: +(performance.now() - t0).toFixed(1),
- agent: data.agent,
- type: data.agent_type
- });
return result;
}
}
diff --git a/src/App/src/api/apiUtils.ts b/src/App/src/api/apiUtils.ts
new file mode 100644
index 000000000..f3872025b
--- /dev/null
+++ b/src/App/src/api/apiUtils.ts
@@ -0,0 +1,149 @@
+/**
+ * API Utility Functions
+ *
+ * Centralized helpers for error response construction, retry logic,
+ * and request deduplication. Single source of truth โ eliminates
+ * duplicated error patterns across API functions.
+ */
+
+/**
+ * Create a standardized error response object.
+ * Replaces repeated `{ ...new Response(), ok: false, status: 500 }` patterns.
+ */
+export function createErrorResponse(status: number, message: string): Response {
+ return new Response(JSON.stringify({ error: message }), {
+ status,
+ statusText: message,
+ headers: { 'Content-Type': 'application/json' },
+ });
+}
+
+/**
+ * Retry a request with exponential backoff.
+ * @param fn - The async function to retry
+ * @param maxRetries - Maximum number of retry attempts (default: 3)
+ * @param baseDelay - Base delay in ms before exponential increase (default: 1000)
+ */
+export async function retryRequest(
+ fn: () => Promise,
+ maxRetries = 3,
+ baseDelay = 1000
+): Promise {
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
+ try {
+ return await fn();
+ } catch (error) {
+ if (attempt === maxRetries) throw error;
+ const delay = baseDelay * Math.pow(2, attempt);
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ }
+ }
+ throw new Error('Max retries exceeded');
+}
+
+/**
+ * Request cache with TTL and deduplication of in-flight requests.
+ * Prevents duplicate API calls for the same data.
+ */
+interface CacheEntry {
+ data: T;
+ timestamp: number;
+ expiresAt: number;
+}
+
+export class RequestCache {
+ private cache = new Map>();
+ private pendingRequests = new Map>();
+
+ /** Get cached data or fetch it, deduplicating concurrent identical requests */
+ async get(
+ key: string,
+ fetcher: () => Promise,
+ ttlMs = 30000
+ ): Promise {
+ // Return cached data if still fresh
+ const cached = this.cache.get(key);
+ if (cached && Date.now() < cached.expiresAt) {
+ return cached.data as T;
+ }
+
+ // Deduplicate concurrent identical requests
+ const pending = this.pendingRequests.get(key);
+ if (pending) {
+ return pending as Promise;
+ }
+
+ const request = fetcher()
+ .then((data) => {
+ this.cache.set(key, {
+ data,
+ timestamp: Date.now(),
+ expiresAt: Date.now() + ttlMs,
+ });
+ this.pendingRequests.delete(key);
+ return data;
+ })
+ .catch((error) => {
+ this.pendingRequests.delete(key);
+ throw error;
+ });
+
+ this.pendingRequests.set(key, request);
+ return request;
+ }
+
+ /** Invalidate cached entries matching a key pattern */
+ invalidate(pattern?: string | RegExp): void {
+ if (!pattern) {
+ this.cache.clear();
+ return;
+ }
+ for (const key of this.cache.keys()) {
+ const matches = typeof pattern === 'string'
+ ? key.includes(pattern)
+ : pattern.test(key);
+ if (matches) this.cache.delete(key);
+ }
+ }
+
+ /** Clear all cached data */
+ clear(): void {
+ this.cache.clear();
+ this.pendingRequests.clear();
+ }
+}
+
+/** Shared request cache singleton */
+export const requestCache = new RequestCache();
+
+/**
+ * Debounce utility โ delays calling `fn` until `delayMs` has elapsed
+ * since the last invocation.
+ */
+export function debounce void>(
+ fn: T,
+ delayMs: number
+): (...args: Parameters) => void {
+ let timer: ReturnType;
+ return (...args: Parameters) => {
+ clearTimeout(timer);
+ timer = setTimeout(() => fn(...args), delayMs);
+ };
+}
+
+/**
+ * Throttle utility โ ensures `fn` is called at most once per `limitMs`.
+ */
+export function throttle void>(
+ fn: T,
+ limitMs: number
+): (...args: Parameters) => void {
+ let lastCall = 0;
+ return (...args: Parameters) => {
+ const now = Date.now();
+ if (now - lastCall >= limitMs) {
+ lastCall = now;
+ fn(...args);
+ }
+ };
+}
diff --git a/src/frontend/src/api/config.tsx b/src/App/src/api/config.tsx
similarity index 79%
rename from src/frontend/src/api/config.tsx
rename to src/App/src/api/config.tsx
index b7609e7ee..d3b216eec 100644
--- a/src/frontend/src/api/config.tsx
+++ b/src/App/src/api/config.tsx
@@ -52,9 +52,6 @@ export async function getUserInfo(): Promise {
try {
const response = await fetch("/.auth/me");
if (!response.ok) {
- console.log(
- "No identity provider found. Access to chat will be blocked."
- );
return {} as UserInfo;
}
const payload = await response.json();
@@ -97,7 +94,6 @@ export function getUserInfoGlobal() {
}
if (!USER_INFO) {
- // console.info('User info not yet configured');
return null;
}
@@ -105,7 +101,6 @@ export function getUserInfoGlobal() {
}
export function getUserId(): string {
- // USER_ID = getUserInfoGlobal()?.user_id || null;
if (!USER_ID) {
USER_ID = getUserInfoGlobal()?.user_id || null;
}
@@ -113,24 +108,6 @@ export function getUserId(): string {
return userId;
}
-/**
- * Build headers with authentication information
- * @param headers Optional additional headers to merge
- * @returns Combined headers object with authentication
- */
-export function headerBuilder(headers?: Record): Record {
- let userId = getUserId();
- //console.log('headerBuilder: Using user ID:', userId);
- let defaultHeaders = {
- "x-ms-client-principal-id": String(userId) || "", // Custom header
- };
- //console.log('headerBuilder: Created headers:', defaultHeaders);
- return {
- ...defaultHeaders,
- ...(headers ? headers : {})
- };
-}
-
export const toBoolean = (value: any): boolean => {
if (typeof value !== 'string') {
return false;
diff --git a/src/App/src/api/httpClient.ts b/src/App/src/api/httpClient.ts
new file mode 100644
index 000000000..866709c34
--- /dev/null
+++ b/src/App/src/api/httpClient.ts
@@ -0,0 +1,246 @@
+/**
+ * Centralized HTTP Client with Interceptors
+ *
+ * Singleton class that wraps all API calls with:
+ * - Automatic auth header injection via request interceptors
+ * - Uniform error handling via response interceptors
+ * - Built-in timeout, configurable base URL, and params serialization
+ *
+ * Eliminates duplicated localStorage/header logic across API functions.
+ */
+import { getUserId } from './config';
+
+type RequestConfig = RequestInit & { url: string };
+type RequestInterceptor = (config: RequestConfig) => RequestConfig;
+type ResponseInterceptor = (response: Response) => Response | Promise;
+
+class HttpClient {
+ private baseUrl: string;
+ private requestInterceptors: RequestInterceptor[] = [];
+ private responseInterceptors: ResponseInterceptor[] = [];
+ private timeout: number;
+
+ constructor(baseUrl = '', timeout = 30000) {
+ this.baseUrl = baseUrl;
+ this.timeout = timeout;
+ }
+
+ /** Set or update the base URL at runtime (after config is loaded) */
+ setBaseUrl(url: string): void {
+ this.baseUrl = url;
+ }
+
+ getBaseUrl(): string {
+ return this.baseUrl;
+ }
+
+ /** Register a request interceptor (runs before every request) */
+ addRequestInterceptor(interceptor: RequestInterceptor): void {
+ this.requestInterceptors.push(interceptor);
+ }
+
+ /** Register a response interceptor (runs after every response) */
+ addResponseInterceptor(interceptor: ResponseInterceptor): void {
+ this.responseInterceptors.push(interceptor);
+ }
+
+ /** Build URL with query parameters */
+ private buildUrl(path: string, params?: Record): string {
+ const base = this.baseUrl ? `${this.baseUrl}${path}` : path;
+ if (!params) return base;
+
+ const searchParams = new URLSearchParams();
+ Object.entries(params).forEach(([key, value]) => {
+ if (value !== undefined && value !== null) {
+ searchParams.append(key, String(value));
+ }
+ });
+
+ const queryString = searchParams.toString();
+ return queryString ? `${base}?${queryString}` : base;
+ }
+
+ /** Core request method โ applies interceptors, timeout, and error handling */
+ private async request(
+ path: string,
+ options: RequestInit & { params?: Record } = {}
+ ): Promise {
+ const { params, ...fetchOptions } = options;
+ const url = this.buildUrl(path, params);
+
+ // Build initial config
+ let config: RequestConfig = { url, ...fetchOptions };
+
+ // Run request interceptors
+ for (const interceptor of this.requestInterceptors) {
+ config = interceptor(config);
+ }
+
+ const { url: finalUrl, ...rest } = config;
+
+ // Timeout via AbortController
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
+
+ try {
+ let response = await fetch(finalUrl, {
+ ...rest,
+ signal: controller.signal,
+ });
+
+ // Run response interceptors
+ for (const interceptor of this.responseInterceptors) {
+ response = await interceptor(response);
+ }
+
+ return response;
+ } finally {
+ clearTimeout(timeoutId);
+ }
+ }
+
+ /** HTTP GET */
+ async get(
+ path: string,
+ config?: { params?: Record; headers?: Record }
+ ): Promise {
+ const response = await this.request(path, {
+ method: 'GET',
+ params: config?.params,
+ headers: config?.headers,
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(errorText || 'Request failed');
+ }
+
+ const isJson = response.headers.get('content-type')?.includes('application/json');
+ return isJson ? response.json() : (null as T);
+ }
+
+ /** HTTP POST */
+ async post(
+ path: string,
+ body?: unknown,
+ config?: { headers?: Record }
+ ): Promise {
+ const response = await this.request(path, {
+ method: 'POST',
+ body: JSON.stringify(body),
+ headers: {
+ 'Content-Type': 'application/json',
+ ...config?.headers,
+ },
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(errorText || 'Request failed');
+ }
+
+ const isJson = response.headers.get('content-type')?.includes('application/json');
+ return isJson ? response.json() : (null as T);
+ }
+
+ /** HTTP PUT */
+ async put(
+ path: string,
+ body?: unknown,
+ config?: { headers?: Record }
+ ): Promise {
+ const response = await this.request(path, {
+ method: 'PUT',
+ body: JSON.stringify(body),
+ headers: {
+ 'Content-Type': 'application/json',
+ ...config?.headers,
+ },
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(errorText || 'Request failed');
+ }
+
+ const isJson = response.headers.get('content-type')?.includes('application/json');
+ return isJson ? response.json() : (null as T);
+ }
+
+ /** HTTP DELETE */
+ async del(path: string): Promise {
+ const response = await this.request(path, { method: 'DELETE' });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(errorText || 'Request failed');
+ }
+
+ const isJson = response.headers.get('content-type')?.includes('application/json');
+ return isJson ? response.json() : (null as T);
+ }
+
+ /** Upload a FormData payload (multipart/form-data) */
+ async upload(path: string, formData: FormData): Promise {
+ // Don't set Content-Type โ browser sets multipart boundary automatically
+ const response = await this.request(path, {
+ method: 'POST',
+ body: formData,
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(errorText || 'Upload failed');
+ }
+
+ const isJson = response.headers.get('content-type')?.includes('application/json');
+ return isJson ? response.json() : (null as T);
+ }
+
+ /** HTTP POST without auth (used for login) */
+ async postWithoutAuth(path: string, body?: unknown): Promise {
+ const url = this.baseUrl ? `${this.baseUrl}${path}` : path;
+
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: body ? JSON.stringify(body) : undefined,
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(errorText || 'Request failed');
+ }
+
+ const isJson = response.headers.get('content-type')?.includes('application/json');
+ return isJson ? response.json() : (null as T);
+ }
+}
+
+// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// Singleton instance with interceptors
+// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+const httpClient = new HttpClient();
+
+/**
+ * Auth interceptor โ single source of truth for userId header.
+ * Eliminates repeated localStorage.getItem("userId") and manual headerBuilder() calls.
+ */
+httpClient.addRequestInterceptor((config) => {
+ const userId = getUserId();
+ const token = localStorage.getItem('token');
+
+ const headers = new Headers(config.headers as HeadersInit);
+
+ if (userId) {
+ headers.set('x-ms-client-principal-id', String(userId));
+ }
+ if (token) {
+ headers.set('Authorization', `Bearer ${token}`);
+ }
+
+ return { ...config, headers };
+});
+
+export default httpClient;
diff --git a/src/App/src/api/index.tsx b/src/App/src/api/index.tsx
new file mode 100644
index 000000000..c88cde5fd
--- /dev/null
+++ b/src/App/src/api/index.tsx
@@ -0,0 +1,11 @@
+// Export our API services and utilities
+export * from './apiClient';
+
+// Centralized HTTP client with interceptors (Point 2)
+export { default as httpClient } from './httpClient';
+
+// API utilities: createErrorResponse, retryRequest, RequestCache (Points 6, 8)
+export * from './apiUtils';
+
+// Unified API service - recommended for all new code
+export { apiService } from './apiService';
diff --git a/src/frontend/src/assets/WebWarning.svg b/src/App/src/assets/WebWarning.svg
similarity index 100%
rename from src/frontend/src/assets/WebWarning.svg
rename to src/App/src/assets/WebWarning.svg
diff --git a/src/frontend/src/coral/SYSTEM_OVERVIEW.md b/src/App/src/commonComponents/SYSTEM_OVERVIEW.md
similarity index 100%
rename from src/frontend/src/coral/SYSTEM_OVERVIEW.md
rename to src/App/src/commonComponents/SYSTEM_OVERVIEW.md
diff --git a/src/frontend/src/coral/components/Content/Chat.css b/src/App/src/commonComponents/components/Content/Chat.css
similarity index 100%
rename from src/frontend/src/coral/components/Content/Chat.css
rename to src/App/src/commonComponents/components/Content/Chat.css
diff --git a/src/frontend/src/coral/components/Content/Content.tsx b/src/App/src/commonComponents/components/Content/Content.tsx
similarity index 95%
rename from src/frontend/src/coral/components/Content/Content.tsx
rename to src/App/src/commonComponents/components/Content/Content.tsx
index 2eb5d3d5d..2f316d7bf 100644
--- a/src/frontend/src/coral/components/Content/Content.tsx
+++ b/src/App/src/commonComponents/components/Content/Content.tsx
@@ -1,4 +1,4 @@
-import React, { useState, ReactNode, ReactElement } from "react";
+import React, { ReactNode, ReactElement } from "react";
import PanelToolbar from "../Panels/PanelLeftToolbar.js"; // Import to identify toolbar
interface ContentProps {
diff --git a/src/frontend/src/coral/components/Content/ContentToolbar.tsx b/src/App/src/commonComponents/components/Content/ContentToolbar.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Content/ContentToolbar.tsx
rename to src/App/src/commonComponents/components/Content/ContentToolbar.tsx
diff --git a/src/frontend/src/coral/components/Content/README.md b/src/App/src/commonComponents/components/Content/README.md
similarity index 100%
rename from src/frontend/src/coral/components/Content/README.md
rename to src/App/src/commonComponents/components/Content/README.md
diff --git a/src/frontend/src/coral/components/CoralAccordion/CoralAccordion.tsx b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordion.tsx
similarity index 100%
rename from src/frontend/src/coral/components/CoralAccordion/CoralAccordion.tsx
rename to src/App/src/commonComponents/components/CoralAccordion/CoralAccordion.tsx
diff --git a/src/frontend/src/coral/components/CoralAccordion/CoralAccordionContext.tsx b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordionContext.tsx
similarity index 100%
rename from src/frontend/src/coral/components/CoralAccordion/CoralAccordionContext.tsx
rename to src/App/src/commonComponents/components/CoralAccordion/CoralAccordionContext.tsx
diff --git a/src/frontend/src/coral/components/CoralAccordion/CoralAccordionHeader.tsx b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordionHeader.tsx
similarity index 93%
rename from src/frontend/src/coral/components/CoralAccordion/CoralAccordionHeader.tsx
rename to src/App/src/commonComponents/components/CoralAccordion/CoralAccordionHeader.tsx
index c6372a423..d4422f2c8 100644
--- a/src/frontend/src/coral/components/CoralAccordion/CoralAccordionHeader.tsx
+++ b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordionHeader.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { Body1 } from "@fluentui/react-components";
-import { ChevronDown20Regular, ChevronUp20Regular } from "@fluentui/react-icons";
+import { ChevronUp20Regular } from "@fluentui/react-icons";
import { useCoralAccordion } from "./CoralAccordionContext";
type Props = {
diff --git a/src/frontend/src/coral/components/CoralAccordion/CoralAccordionItem.tsx b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordionItem.tsx
similarity index 93%
rename from src/frontend/src/coral/components/CoralAccordion/CoralAccordionItem.tsx
rename to src/App/src/commonComponents/components/CoralAccordion/CoralAccordionItem.tsx
index a25e98357..20954e028 100644
--- a/src/frontend/src/coral/components/CoralAccordion/CoralAccordionItem.tsx
+++ b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordionItem.tsx
@@ -1,4 +1,4 @@
-import React, { useState, cloneElement, isValidElement } from "react";
+import React, { useState, isValidElement } from "react";
import { CoralAccordionContext } from "./CoralAccordionContext";
type CoralAccordionItemProps = {
diff --git a/src/frontend/src/coral/components/CoralAccordion/CoralAccordionPanel.tsx b/src/App/src/commonComponents/components/CoralAccordion/CoralAccordionPanel.tsx
similarity index 100%
rename from src/frontend/src/coral/components/CoralAccordion/CoralAccordionPanel.tsx
rename to src/App/src/commonComponents/components/CoralAccordion/CoralAccordionPanel.tsx
diff --git a/src/frontend/src/coral/components/Header/Header.tsx b/src/App/src/commonComponents/components/Header/Header.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Header/Header.tsx
rename to src/App/src/commonComponents/components/Header/Header.tsx
diff --git a/src/frontend/src/coral/components/Header/HeaderTools.tsx b/src/App/src/commonComponents/components/Header/HeaderTools.tsx
similarity index 66%
rename from src/frontend/src/coral/components/Header/HeaderTools.tsx
rename to src/App/src/commonComponents/components/Header/HeaderTools.tsx
index 0df091905..2e3c3a610 100644
--- a/src/frontend/src/coral/components/Header/HeaderTools.tsx
+++ b/src/App/src/commonComponents/components/Header/HeaderTools.tsx
@@ -1,8 +1,5 @@
-import React, { useState } from "react";
-import { Toolbar, ToolbarDivider, Avatar } from "@fluentui/react-components";
-import eventBus from "../eventbus";
-import PanelRightToggles from "./PanelRightToggles"; // Import PanelRightToggles
-
+import React from "react";
+import { Toolbar } from "@fluentui/react-components";
interface HeaderToolsProps {
children?: React.ReactNode;
diff --git a/src/frontend/src/coral/components/Header/PanelRightToggles.tsx b/src/App/src/commonComponents/components/Header/PanelRightToggles.tsx
similarity index 96%
rename from src/frontend/src/coral/components/Header/PanelRightToggles.tsx
rename to src/App/src/commonComponents/components/Header/PanelRightToggles.tsx
index 938777226..2c0c0f4c5 100644
--- a/src/frontend/src/coral/components/Header/PanelRightToggles.tsx
+++ b/src/App/src/commonComponents/components/Header/PanelRightToggles.tsx
@@ -12,7 +12,7 @@ import {
Button,
ButtonProps
} from "@fluentui/react-components";
-import { PanelRightContract, PanelRightExpand } from "@/coral/imports/bundleicons";
+import { PanelRightContract, PanelRightExpand } from "@/commonComponents/imports/bundleicons";
import eventBus from "../eventbus.js";
type PanelRightTogglesProps = {
diff --git a/src/frontend/src/coral/components/Header/README.md b/src/App/src/commonComponents/components/Header/README.md
similarity index 100%
rename from src/frontend/src/coral/components/Header/README.md
rename to src/App/src/commonComponents/components/Header/README.md
diff --git a/src/frontend/src/coral/components/Layout/CoralShellColumn.tsx b/src/App/src/commonComponents/components/Layout/CoralShellColumn.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Layout/CoralShellColumn.tsx
rename to src/App/src/commonComponents/components/Layout/CoralShellColumn.tsx
diff --git a/src/frontend/src/coral/components/Layout/CoralShellRow.tsx b/src/App/src/commonComponents/components/Layout/CoralShellRow.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Layout/CoralShellRow.tsx
rename to src/App/src/commonComponents/components/Layout/CoralShellRow.tsx
diff --git a/src/frontend/src/coral/components/Layout/README.md b/src/App/src/commonComponents/components/Layout/README.md
similarity index 100%
rename from src/frontend/src/coral/components/Layout/README.md
rename to src/App/src/commonComponents/components/Layout/README.md
diff --git a/src/frontend/src/coral/components/LoadingMessage.tsx b/src/App/src/commonComponents/components/LoadingMessage.tsx
similarity index 100%
rename from src/frontend/src/coral/components/LoadingMessage.tsx
rename to src/App/src/commonComponents/components/LoadingMessage.tsx
diff --git a/src/frontend/src/coral/components/Panels/PanelFooter.tsx b/src/App/src/commonComponents/components/Panels/PanelFooter.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Panels/PanelFooter.tsx
rename to src/App/src/commonComponents/components/Panels/PanelFooter.tsx
diff --git a/src/frontend/src/coral/components/Panels/PanelLeft.tsx b/src/App/src/commonComponents/components/Panels/PanelLeft.tsx
similarity index 95%
rename from src/frontend/src/coral/components/Panels/PanelLeft.tsx
rename to src/App/src/commonComponents/components/Panels/PanelLeft.tsx
index bc83cbe78..19c124eae 100644
--- a/src/frontend/src/coral/components/Panels/PanelLeft.tsx
+++ b/src/App/src/commonComponents/components/Panels/PanelLeft.tsx
@@ -7,13 +7,6 @@ import React, {
} from "react";
import PanelToolbar from "./PanelLeftToolbar.js";
import PanelFooter from "./PanelFooter"; // ๐ new
-import {
- Avatar,
- Body1,
- Body1Strong,
- Caption1,
-} from "@fluentui/react-components";
-import Human from "../../imports/human.png";
interface PanelLeftProps {
panelWidth?: number;
diff --git a/src/frontend/src/coral/components/Panels/PanelLeftToolbar.tsx b/src/App/src/commonComponents/components/Panels/PanelLeftToolbar.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Panels/PanelLeftToolbar.tsx
rename to src/App/src/commonComponents/components/Panels/PanelLeftToolbar.tsx
diff --git a/src/frontend/src/coral/components/Panels/PanelRight.tsx b/src/App/src/commonComponents/components/Panels/PanelRight.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Panels/PanelRight.tsx
rename to src/App/src/commonComponents/components/Panels/PanelRight.tsx
diff --git a/src/frontend/src/coral/components/Panels/PanelRightToolbar.tsx b/src/App/src/commonComponents/components/Panels/PanelRightToolbar.tsx
similarity index 88%
rename from src/frontend/src/coral/components/Panels/PanelRightToolbar.tsx
rename to src/App/src/commonComponents/components/Panels/PanelRightToolbar.tsx
index 0fc3285f0..2861a5bf9 100644
--- a/src/frontend/src/coral/components/Panels/PanelRightToolbar.tsx
+++ b/src/App/src/commonComponents/components/Panels/PanelRightToolbar.tsx
@@ -16,9 +16,14 @@ const PanelRightToolbar: React.FC = ({
panelIcon,
// panelType = "first", // Default value set here
children,
+ handleDismiss,
}) => {
- const handleDismiss = () => {
- eventBus.emit("setActivePanel", null); // Close the current panel
+ const onDismiss = () => {
+ if (handleDismiss) {
+ handleDismiss();
+ } else {
+ eventBus.emit("setActivePanel", null); // Close the current panel
+ }
};
return (
@@ -76,12 +81,12 @@ const PanelRightToolbar: React.FC = ({
}}
>
{children}
- {/* }
- onClick={handleDismiss} // Handle dismiss logic
+ onClick={onDismiss} // Handle dismiss logic
aria-label="Close panel"
- /> */}
+ />
);
diff --git a/src/frontend/src/coral/components/Panels/README.md b/src/App/src/commonComponents/components/Panels/README.md
similarity index 100%
rename from src/frontend/src/coral/components/Panels/README.md
rename to src/App/src/commonComponents/components/Panels/README.md
diff --git a/src/frontend/src/coral/components/Panels/UserCard.tsx b/src/App/src/commonComponents/components/Panels/UserCard.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Panels/UserCard.tsx
rename to src/App/src/commonComponents/components/Panels/UserCard.tsx
diff --git a/src/frontend/src/coral/components/Progress/ProgressCircle.tsx b/src/App/src/commonComponents/components/Progress/ProgressCircle.tsx
similarity index 100%
rename from src/frontend/src/coral/components/Progress/ProgressCircle.tsx
rename to src/App/src/commonComponents/components/Progress/ProgressCircle.tsx
diff --git a/src/frontend/src/coral/components/PromptCard.tsx b/src/App/src/commonComponents/components/PromptCard.tsx
similarity index 100%
rename from src/frontend/src/coral/components/PromptCard.tsx
rename to src/App/src/commonComponents/components/PromptCard.tsx
diff --git a/src/frontend/src/coral/components/eventbus.README.md b/src/App/src/commonComponents/components/eventbus.README.md
similarity index 100%
rename from src/frontend/src/coral/components/eventbus.README.md
rename to src/App/src/commonComponents/components/eventbus.README.md
diff --git a/src/frontend/src/coral/components/eventbus.tsx b/src/App/src/commonComponents/components/eventbus.tsx
similarity index 100%
rename from src/frontend/src/coral/components/eventbus.tsx
rename to src/App/src/commonComponents/components/eventbus.tsx
diff --git a/src/frontend/src/coral/imports/ContosoLogo.tsx b/src/App/src/commonComponents/imports/ContosoLogo.tsx
similarity index 100%
rename from src/frontend/src/coral/imports/ContosoLogo.tsx
rename to src/App/src/commonComponents/imports/ContosoLogo.tsx
diff --git a/src/frontend/src/coral/imports/MsftColor.tsx b/src/App/src/commonComponents/imports/MsftColor.tsx
similarity index 100%
rename from src/frontend/src/coral/imports/MsftColor.tsx
rename to src/App/src/commonComponents/imports/MsftColor.tsx
diff --git a/src/frontend/src/coral/imports/Octopus.png b/src/App/src/commonComponents/imports/Octopus.png
similarity index 100%
rename from src/frontend/src/coral/imports/Octopus.png
rename to src/App/src/commonComponents/imports/Octopus.png
diff --git a/src/frontend/src/coral/imports/bundleicons.tsx b/src/App/src/commonComponents/imports/bundleicons.tsx
similarity index 100%
rename from src/frontend/src/coral/imports/bundleicons.tsx
rename to src/App/src/commonComponents/imports/bundleicons.tsx
diff --git a/src/frontend/src/coral/imports/human.png b/src/App/src/commonComponents/imports/human.png
similarity index 100%
rename from src/frontend/src/coral/imports/human.png
rename to src/App/src/commonComponents/imports/human.png
diff --git a/src/frontend/src/coral/modules/Chat.css b/src/App/src/commonComponents/modules/Chat.css
similarity index 100%
rename from src/frontend/src/coral/modules/Chat.css
rename to src/App/src/commonComponents/modules/Chat.css
diff --git a/src/frontend/src/coral/modules/Chat.tsx b/src/App/src/commonComponents/modules/Chat.tsx
similarity index 93%
rename from src/frontend/src/coral/modules/Chat.tsx
rename to src/App/src/commonComponents/modules/Chat.tsx
index 74f5590e7..c5617b8d4 100644
--- a/src/frontend/src/coral/modules/Chat.tsx
+++ b/src/App/src/commonComponents/modules/Chat.tsx
@@ -6,7 +6,6 @@ import {
Body1,
Button,
Tag,
- Tooltip as FluentTooltip,
ToolbarDivider,
} from "@fluentui/react-components";
import { Copy, Send } from "../imports/bundleicons";
@@ -47,7 +46,6 @@ const Chat: React.FC = ({
const [isTyping, setIsTyping] = useState(false);
const [showScrollButton, setShowScrollButton] = useState(false);
const [inputHeight, setInputHeight] = useState(0);
- const [currentConversationId, setCurrentConversationId] = useState(undefined);
const messagesContainerRef = useRef(null);
const inputContainerRef = useRef(null);
@@ -64,8 +62,9 @@ const Chat: React.FC = ({
}
// const chatMessages = await chatService.getUserHistory(userId);
// setMessages(chatMessages);
- } catch (err) {
- console.log("Failed to load chat history.", err);
+ } catch {
+ // Failed to load history โ silent fail
+ console.log("Failed to load chat history for user");
}
};
loadHistory();
@@ -104,8 +103,8 @@ const Chat: React.FC = ({
};
const handleCopy = (text: string) => {
- navigator.clipboard.writeText(text).catch((err) => {
- console.log("Failed to copy text:", err);
+ navigator.clipboard.writeText(text).catch(() => {
+ // clipboard copy failed โ silent
});
};
@@ -152,8 +151,7 @@ const Chat: React.FC = ({
// const assistantMessage = { role: "assistant", content: response.assistant_response };
// setMessages([...updatedMessages, assistantMessage]);
}
- } catch (err) {
- console.log("Send Message Error:", err);
+ } catch {
setMessages([
...updatedMessages,
{ role: "assistant", content: "Oops! Something went wrong sending your message." },
@@ -171,9 +169,9 @@ const Chat: React.FC = ({
// await chatService.clearChatHistory(userId);
}
setMessages([]);
- setCurrentConversationId(undefined);
- } catch (err) {
- console.log("Failed to clear chat history:", err);
+ } catch {
+ // clear history failed โ silent
+ console.log("Failed to clear chat history for user");
}
};
@@ -198,7 +196,7 @@ const Chat: React.FC = ({
icon={ }
/>
console.log("Heart clicked for response:", msg.content)}
+ onClick={() => {}}
title="Like"
appearance="subtle"
style={{ height: 28, width: 28 }}
diff --git a/src/frontend/src/coral/modules/ChatExample.tsx b/src/App/src/commonComponents/modules/ChatExample.tsx
similarity index 100%
rename from src/frontend/src/coral/modules/ChatExample.tsx
rename to src/App/src/commonComponents/modules/ChatExample.tsx
diff --git a/src/frontend/src/coral/modules/ChatInput.tsx b/src/App/src/commonComponents/modules/ChatInput.tsx
similarity index 97%
rename from src/frontend/src/coral/modules/ChatInput.tsx
rename to src/App/src/commonComponents/modules/ChatInput.tsx
index 43d21518e..02ff0155c 100644
--- a/src/frontend/src/coral/modules/ChatInput.tsx
+++ b/src/App/src/commonComponents/modules/ChatInput.tsx
@@ -1,16 +1,11 @@
import React, {
useRef,
useState,
- useEffect,
forwardRef,
useImperativeHandle,
useLayoutEffect,
} from "react";
-import {
- Tag,
- Tooltip as FluentTooltip,
- Caption1,
-} from "@fluentui/react-components";
+import { Caption1 } from "@fluentui/react-components";
import HeaderTools from "../components/Header/HeaderTools";
// โ
Props definition
diff --git a/src/frontend/src/coral/modules/prism-material-oceanic.css b/src/App/src/commonComponents/modules/prism-material-oceanic.css
similarity index 100%
rename from src/frontend/src/coral/modules/prism-material-oceanic.css
rename to src/App/src/commonComponents/modules/prism-material-oceanic.css
diff --git a/src/frontend/src/components/NotFound/ContentNotFound.tsx b/src/App/src/components/NotFound/ContentNotFound.tsx
similarity index 100%
rename from src/frontend/src/components/NotFound/ContentNotFound.tsx
rename to src/App/src/components/NotFound/ContentNotFound.tsx
diff --git a/src/frontend/src/components/common/PlanCancellationDialog.tsx b/src/App/src/components/common/PlanCancellationDialog.tsx
similarity index 100%
rename from src/frontend/src/components/common/PlanCancellationDialog.tsx
rename to src/App/src/components/common/PlanCancellationDialog.tsx
diff --git a/src/frontend/src/components/common/TeamSelected.tsx b/src/App/src/components/common/TeamSelected.tsx
similarity index 100%
rename from src/frontend/src/components/common/TeamSelected.tsx
rename to src/App/src/components/common/TeamSelected.tsx
diff --git a/src/frontend/src/components/common/TeamSelector.tsx b/src/App/src/components/common/TeamSelector.tsx
similarity index 96%
rename from src/frontend/src/components/common/TeamSelector.tsx
rename to src/App/src/components/common/TeamSelector.tsx
index ae04bf8ec..21e177fe5 100644
--- a/src/frontend/src/components/common/TeamSelector.tsx
+++ b/src/App/src/components/common/TeamSelector.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState } from 'react';
import {
Button,
Dialog,
@@ -9,9 +9,7 @@ import {
DialogBody,
Text,
Spinner,
- Card,
Body1,
- Body2,
Caption1,
Badge,
Input,
@@ -22,7 +20,6 @@ import {
Tooltip,
DialogOpenChangeEvent,
DialogOpenChangeData,
- TabValue,
SelectTabEvent,
SelectTabData,
InputOnChangeData
@@ -36,7 +33,7 @@ import {
Delete20Filled
} from '@fluentui/react-icons';
import { TeamConfig } from '../../models/Team';
-import { TeamService } from '../../services/TeamService';
+import { TeamService } from '../../store/TeamService';
import styles from '../../styles/TeamSelector.module.css';
@@ -70,8 +67,8 @@ const TeamSelector: React.FC = ({
const [uploadSuccessMessage, setUploadSuccessMessage] = useState(null);
// Helper function to check if a team is a default team
const isDefaultTeam = (team: TeamConfig): boolean => {
- const defaultTeamIds = ['team-1', 'team-2', 'team-3'];
- const defaultTeamNames = ['Human Resources Team', 'Product Marketing Team', 'Retail Customer Success Team'];
+ const defaultTeamIds = ['team-1', 'team-2', 'team-3','team-clm-1', 'team-compliance-1'];
+ const defaultTeamNames = ['Human Resources Team', 'Product Marketing Team', 'Retail Customer Success Team','RFP Team', 'Contract Compliance Review Team'];
return defaultTeamIds.includes(team.team_id) ||
defaultTeamNames.includes(team.name);
@@ -119,7 +116,6 @@ const TeamSelector: React.FC = ({
try {
// If this team was just uploaded, skip the selection API call and go directly to homepage
if (uploadedTeam && uploadedTeam.team_id === tempSelectedTeam.team_id) {
- console.log('Uploaded team selected, going directly to homepage:', tempSelectedTeam.name);
onTeamSelect?.(tempSelectedTeam);
setIsOpen(false);
return; // Skip the selectTeam API call
@@ -129,14 +125,12 @@ const TeamSelector: React.FC = ({
const result = await TeamService.selectTeam(tempSelectedTeam.team_id);
if (result.success) {
- console.log('Team selected:', result.data);
onTeamSelect?.(tempSelectedTeam);
setIsOpen(false);
} else {
setError(result.error || 'Failed to select team');
}
- } catch (err: any) {
- console.error('Error selecting team:', err);
+ } catch {
setError('Failed to select team. Please try again.');
} finally {
setSelectionLoading(false);
@@ -246,7 +240,7 @@ const TeamSelector: React.FC = ({
let teamData;
try {
teamData = JSON.parse(fileText);
- } catch (parseError) {
+ } catch {
throw new Error('Invalid JSON file format');
}
@@ -347,7 +341,7 @@ const TeamSelector: React.FC = ({
let teamData;
try {
teamData = JSON.parse(fileText);
- } catch (parseError) {
+ } catch {
throw new Error('Invalid JSON file format');
}
@@ -566,7 +560,6 @@ const TeamSelector: React.FC = ({
placeholder="Search teams..."
value={searchQuery}
onChange={(e: React.ChangeEvent, data: InputOnChangeData) => {
- console.log('Search changed:', data.value);
setSearchQuery(data.value || '');
}}
contentBefore={ }
@@ -707,18 +700,16 @@ const TeamSelector: React.FC = ({
- {tempSelectedTeam && (
-
-
- {selectionLoading ? 'Selecting...' : 'Continue'}
-
-
- )}
+
+
+ {selectionLoading ? 'Selecting...' : 'Continue'}
+
+
diff --git a/src/App/src/components/content/HomeInput.tsx b/src/App/src/components/content/HomeInput.tsx
new file mode 100644
index 000000000..b4cbba2a9
--- /dev/null
+++ b/src/App/src/components/content/HomeInput.tsx
@@ -0,0 +1,293 @@
+import {
+ Body1Strong,
+ Button,
+ Caption1,
+ Title2
+} from "@fluentui/react-components";
+
+import React, { useRef, useEffect, useState } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+
+import "./../../styles/Chat.css";
+import "../../styles/prism-material-oceanic.css";
+import "./../../styles/HomeInput.css";
+
+import { HomeInputProps, iconMap, QuickTask } from "../../models/homeInput";
+import { TaskService } from "../../store/TaskService";
+import { NewTaskService } from "../../store/NewTaskService";
+
+import ChatInput from "@/commonComponents/modules/ChatInput";
+import InlineToaster, { useInlineToaster } from "../toast/InlineToaster";
+import PromptCard from "@/commonComponents/components/PromptCard";
+import { Send } from "@/commonComponents/imports/bundleicons";
+import { Clipboard20Regular } from "@fluentui/react-icons";
+
+// Icon mapping function to convert string icons to FluentUI icons
+const getIconFromString = (
+ iconString: string | React.ReactNode
+): React.ReactNode => {
+ // If it's already a React node, return it
+ if (typeof iconString !== "string") {
+ return iconString;
+ }
+
+ return iconMap[iconString] || iconMap["default"] || ;
+};
+
+const truncateDescription = (
+ description: string,
+ maxLength: number = 180
+): string => {
+ if (!description) return "";
+
+ if (description.length <= maxLength) {
+ return description;
+ }
+
+ const truncated = description.substring(0, maxLength);
+ const lastSpaceIndex = truncated.lastIndexOf(" ");
+
+ const cutPoint = lastSpaceIndex > maxLength - 20 ? lastSpaceIndex : maxLength;
+
+ return description.substring(0, cutPoint) + "...";
+};
+
+// Extended QuickTask interface to store both truncated and full descriptions
+interface ExtendedQuickTask extends QuickTask {
+ fullDescription: string; // Store the full, untruncated description
+}
+
+const HomeInput: React.FC = ({ selectedTeam }) => {
+ const [submitting, setSubmitting] = useState(false);
+ const [input, setInput] = useState("");
+
+ const textareaRef = useRef(null);
+ const navigate = useNavigate();
+ const location = useLocation(); // โ
location.state used to control focus
+ const { showToast, dismissToast } = useInlineToaster();
+
+ // Check if the selected team is the Contract Compliance Review Team
+ const isLegalTeam = selectedTeam?.name
+ ?.toLowerCase()
+ .includes("contract compliance");
+
+ useEffect(() => {
+ if (location.state?.focusInput) {
+ textareaRef.current?.focus();
+ }
+ }, [location]);
+
+ const resetTextarea = () => {
+ setInput("");
+ if (textareaRef.current) {
+ textareaRef.current.style.height = "auto";
+ textareaRef.current.focus();
+ }
+ };
+
+ useEffect(() => {
+ const cleanup = NewTaskService.addResetListener(resetTextarea);
+ return cleanup;
+ }, []);
+
+ const handleSubmit = async () => {
+ if (input.trim()) {
+ setSubmitting(true);
+ let id = showToast("Creating a plan", "progress");
+
+ try {
+ const response = await TaskService.createPlan(
+ input.trim(),
+ selectedTeam?.team_id
+ );
+ setInput("");
+
+ if (textareaRef.current) {
+ textareaRef.current.style.height = "auto";
+ }
+
+ if (response.plan_id && response.plan_id !== null) {
+ showToast("Plan created!", "success");
+ dismissToast(id);
+
+ navigate(`/plan/${response.plan_id}`);
+ } else {
+ showToast("Failed to create plan", "error");
+ dismissToast(id);
+ }
+ } catch (error: any) {
+ let errorMessage = "Unable to create plan. Please try again.";
+ dismissToast(id);
+ // Check if this is an RAI validation error
+ try {
+ // errorDetail = JSON.parse(error);
+ errorMessage = error?.message || errorMessage;
+ } catch (parseError) {
+ console.error("Error parsing error response", parseError);
+ }
+
+ showToast(errorMessage, "error");
+ } finally {
+ setInput("");
+ setSubmitting(false);
+ }
+ }
+ };
+
+ const handleQuickTaskClick = (task: ExtendedQuickTask) => {
+ setInput(task.fullDescription);
+ if (textareaRef.current) {
+ textareaRef.current.focus();
+ }
+ };
+
+ useEffect(() => {
+ if (textareaRef.current) {
+ textareaRef.current.style.height = "auto";
+ textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
+ }
+ }, [input]);
+
+ // Convert team starting_tasks to ExtendedQuickTask format
+ const tasksToDisplay: ExtendedQuickTask[] =
+ selectedTeam && selectedTeam.starting_tasks
+ ? selectedTeam.starting_tasks.map((task, index) => {
+ // Handle both string tasks and StartingTask objects
+ if (typeof task === "string") {
+ return {
+ id: `team-task-${index}`,
+ title: task,
+ description: truncateDescription(task),
+ fullDescription: task, // Store the full description
+ icon: getIconFromString("๐"),
+ };
+ } else {
+ // Handle StartingTask objects
+ const startingTask = task as any; // Type assertion for now
+ const taskDescription =
+ startingTask.prompt || startingTask.name || "Task description";
+ return {
+ id: startingTask.id || `team-task-${index}`,
+ title: startingTask.name || startingTask.prompt || "Task",
+ description: truncateDescription(taskDescription),
+ fullDescription: taskDescription, // Store the full description
+ icon: getIconFromString(startingTask.logo || "๐"),
+ };
+ }
+ })
+ : [];
+
+ return (
+
+
+
+
+ How can I help?
+
+
+ {/* Legal Disclaimer for Contract Compliance Review Team */}
+ {isLegalTeam && (
+
+
+ Disclaimer: This tool is not intended to give
+ legal advice; it is intended solely for the purpose of assessing
+ contract compliance against internal guidance and policy frameworks.
+
+
+ )}
+
+ {/* Show RAI error if present */}
+ {/* {raiError && (
+
{
+ setRAIError(null);
+ if (textareaRef.current) {
+ textareaRef.current.focus();
+ }
+ }}
+ onDismiss={() => setRAIError(null)}
+ />
+ )} */}
+
+
+ }
+ />
+
+
+
+
+
+ {tasksToDisplay.length > 0 && (
+ <>
+
+ Quick tasks
+
+
+
+
+ {tasksToDisplay.map((task) => (
+
handleQuickTaskClick(task)}
+ disabled={submitting}
+ />
+ ))}
+
+
+ >
+ )}
+ {tasksToDisplay.length === 0 && selectedTeam && (
+
+ No starting tasks available for this team
+
+ )}
+ {!selectedTeam && (
+
+ Select a team to see available tasks
+
+ )}
+
+
+
+
+ );
+};
+
+const MemoizedHomeInput = React.memo(HomeInput);
+MemoizedHomeInput.displayName = 'HomeInput';
+export default MemoizedHomeInput;
diff --git a/src/frontend/src/components/content/PlanChat.tsx b/src/App/src/components/content/PlanChat.tsx
similarity index 78%
rename from src/frontend/src/components/content/PlanChat.tsx
rename to src/App/src/components/content/PlanChat.tsx
index 57a1c8057..2a61e21ce 100644
--- a/src/frontend/src/components/content/PlanChat.tsx
+++ b/src/App/src/components/content/PlanChat.tsx
@@ -1,31 +1,7 @@
-import React, { useState, useRef, useCallback, useEffect } from "react";
-import {
- Button,
- Body1,
- Spinner,
- Tag,
- Textarea,
-} from "@fluentui/react-components";
-import ReactMarkdown from "react-markdown";
-import remarkGfm from "remark-gfm";
-import {
- CheckmarkRegular,
- DismissRegular,
- SendRegular,
- PersonRegular,
- BotRegular,
-} from "@fluentui/react-icons";
+import React from "react";
import { PlanChatProps, MPlanData } from "../../models/plan";
-import webSocketService from "../../services/WebSocketService";
-import { PlanDataService } from "../../services/PlanDataService";
-import { apiService } from "../../api/apiService";
-import { useNavigate } from "react-router-dom";
-import ChatInput from "../../coral/modules/ChatInput";
-import InlineToaster, {
- useInlineToaster,
-} from "../toast/InlineToaster";
-import { AgentMessageData, WebsocketMessageType } from "@/models";
-import getUserPlan from "./streaming/StreamingUserPlan";
+import InlineToaster from "../toast/InlineToaster";
+import { AgentMessageData } from "@/models";
import renderUserPlanMessage from "./streaming/StreamingUserPlanMessage";
import renderPlanResponse from "./streaming/StreamingPlanResponse";
import { renderPlanExecutionMessage, renderThinkingState } from "./streaming/StreamingPlanState";
@@ -132,4 +108,6 @@ const PlanChat: React.FC = ({
);
};
-export default PlanChat;
\ No newline at end of file
+const MemoizedPlanChat = React.memo(PlanChat);
+MemoizedPlanChat.displayName = 'PlanChat';
+export default MemoizedPlanChat;
\ No newline at end of file
diff --git a/src/frontend/src/components/content/PlanChatBody.tsx b/src/App/src/components/content/PlanChatBody.tsx
similarity index 87%
rename from src/frontend/src/components/content/PlanChatBody.tsx
rename to src/App/src/components/content/PlanChatBody.tsx
index 5848e8724..25df05d83 100644
--- a/src/frontend/src/components/content/PlanChatBody.tsx
+++ b/src/App/src/components/content/PlanChatBody.tsx
@@ -1,7 +1,8 @@
-import ChatInput from "@/coral/modules/ChatInput";
+import React from "react";
+import ChatInput from "@/commonComponents/modules/ChatInput";
import { PlanChatProps } from "@/models";
-import { Button, Caption1 } from "@fluentui/react-components";
-import { Send } from "@/coral/imports/bundleicons";
+import { Button } from "@fluentui/react-components";
+import { Send } from "@/commonComponents/imports/bundleicons";
interface SimplifiedPlanChatProps extends PlanChatProps {
planData: any;
@@ -74,4 +75,6 @@ const PlanChatBody: React.FC = ({
);
}
-export default PlanChatBody;
\ No newline at end of file
+const MemoizedPlanChatBody = React.memo(PlanChatBody);
+MemoizedPlanChatBody.displayName = 'PlanChatBody';
+export default MemoizedPlanChatBody;
\ No newline at end of file
diff --git a/src/frontend/src/components/content/PlanPanelLeft.tsx b/src/App/src/components/content/PlanPanelLeft.tsx
similarity index 89%
rename from src/frontend/src/components/content/PlanPanelLeft.tsx
rename to src/App/src/components/content/PlanPanelLeft.tsx
index 3ed3e483f..d9bfe023c 100644
--- a/src/frontend/src/components/content/PlanPanelLeft.tsx
+++ b/src/App/src/components/content/PlanPanelLeft.tsx
@@ -1,5 +1,6 @@
-import PanelLeft from "@/coral/components/Panels/PanelLeft";
-import PanelLeftToolbar from "@/coral/components/Panels/PanelLeftToolbar";
+import React from "react";
+import PanelLeft from "@/commonComponents/components/Panels/PanelLeft";
+import PanelLeftToolbar from "@/commonComponents/components/Panels/PanelLeftToolbar";
import {
Body1Strong,
Toast,
@@ -17,16 +18,15 @@ import { useCallback, useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Plan, PlanPanelLefProps, Task, UserInfo } from "@/models";
import { apiService } from "@/api";
-import { TaskService } from "@/services";
-import ContosoLogo from "../../coral/imports/ContosoLogo";
+import { TaskService } from "@/store";
+import ContosoLogo from "../../commonComponents/imports/ContosoLogo";
import "../../styles/PlanPanelLeft.css";
-import PanelFooter from "@/coral/components/Panels/PanelFooter";
-import PanelUserCard from "../../coral/components/Panels/UserCard";
+import PanelFooter from "@/commonComponents/components/Panels/PanelFooter";
+import PanelUserCard from "../../commonComponents/components/Panels/UserCard";
import { getUserInfoGlobal } from "@/api/config";
import TeamSelector from "../common/TeamSelector";
import { TeamConfig } from "../../models/Team";
import TeamSelected from "../common/TeamSelected";
-import TeamService from "@/services/TeamService";
const PlanPanelLeft: React.FC = ({
reloadTasks,
@@ -36,13 +36,13 @@ const PlanPanelLeft: React.FC = ({
onTeamUpload,
isHomePage,
selectedTeam: parentSelectedTeam,
- onNavigationWithAlert
+ onNavigationWithAlert,
+ isLoadingTeam
}) => {
const { dispatchToast } = useToastController("toast");
const navigate = useNavigate();
const { planId } = useParams<{ planId: string }>();
- const [inProgressTasks, setInProgressTasks] = useState([]);
const [completedTasks, setCompletedTasks] = useState([]);
const [plans, setPlans] = useState(null);
const [plansLoading, setPlansLoading] = useState(false);
@@ -57,7 +57,6 @@ const PlanPanelLeft: React.FC = ({
const loadPlansData = useCallback(async (forceRefresh = false) => {
try {
- console.log("Loading plans, forceRefresh:", forceRefresh);
setPlansLoading(true);
setPlansError(null);
const plansData = await apiService.getPlans(undefined, !forceRefresh); // Invert forceRefresh for useCache
@@ -68,7 +67,6 @@ const PlanPanelLeft: React.FC = ({
restReload();
}
} catch (error) {
- console.log("Failed to load plans:", error);
setPlansError(
error instanceof Error ? error : new Error("Failed to load plans")
);
@@ -93,16 +91,14 @@ const PlanPanelLeft: React.FC = ({
useEffect(() => {
- console.log("Reload tasks changed:", reloadTasks);
if (reloadTasks) {
loadPlansData(true); // Force refresh when reloadTasks is true
}
}, [loadPlansData, setUserInfo, reloadTasks]);
useEffect(() => {
if (plans) {
- const { inProgress, completed } =
+ const { completed } =
TaskService.transformPlansToTasks(plans);
- setInProgressTasks(inProgress);
setCompletedTasks(completed);
}
}, [plans]);
@@ -220,7 +216,7 @@ const PlanPanelLeft: React.FC = ({
{!isHomePage && (
)}
@@ -249,6 +245,7 @@ const PlanPanelLeft: React.FC = ({
onTaskSelect={handleTaskSelect}
loading={plansLoading}
selectedTaskId={selectedTaskId ?? undefined}
+ isLoadingTeam={isLoadingTeam}
/>
@@ -266,4 +263,6 @@ const PlanPanelLeft: React.FC = ({
);
};
-export default PlanPanelLeft;
+const MemoizedPlanPanelLeft = React.memo(PlanPanelLeft);
+MemoizedPlanPanelLeft.displayName = 'PlanPanelLeft';
+export default MemoizedPlanPanelLeft;
diff --git a/src/frontend/src/components/content/PlanPanelRight.tsx b/src/App/src/components/content/PlanPanelRight.tsx
similarity index 94%
rename from src/frontend/src/components/content/PlanPanelRight.tsx
rename to src/App/src/components/content/PlanPanelRight.tsx
index bf05d5f00..6072b471e 100644
--- a/src/frontend/src/components/content/PlanPanelRight.tsx
+++ b/src/App/src/components/content/PlanPanelRight.tsx
@@ -5,7 +5,7 @@ import {
import {
ArrowTurnDownRightRegular,
} from "@fluentui/react-icons";
-import { MPlanData, PlanDetailsProps } from "../../models";
+import { PlanDetailsProps } from "../../models";
import { getAgentIcon, getAgentDisplayNameWithSuffix } from '../../utils/agentIconUtils';
import ContentNotFound from "../NotFound/ContentNotFound";
import "../../styles/planpanelright.css";
@@ -136,4 +136,6 @@ const PlanPanelRight: React.FC = ({
);
};
-export default PlanPanelRight;
\ No newline at end of file
+const MemoizedPlanPanelRight = React.memo(PlanPanelRight);
+MemoizedPlanPanelRight.displayName = 'PlanPanelRight';
+export default MemoizedPlanPanelRight;
\ No newline at end of file
diff --git a/src/frontend/src/components/content/TaskList.tsx b/src/App/src/components/content/TaskList.tsx
similarity index 93%
rename from src/frontend/src/components/content/TaskList.tsx
rename to src/App/src/components/content/TaskList.tsx
index 9152c3487..4a26f027f 100644
--- a/src/frontend/src/components/content/TaskList.tsx
+++ b/src/App/src/components/content/TaskList.tsx
@@ -22,6 +22,7 @@ const TaskList: React.FC = ({
onTaskSelect,
loading,
selectedTaskId,
+ isLoadingTeam
}) => {
const renderTaskItem = (task: Task) => {
const isActive = task.id === selectedTaskId;
@@ -84,7 +85,7 @@ const TaskList: React.FC = ({
Completed
- {loading
+ {(loading || isLoadingTeam)
? Array.from({ length: 5 }, (_, i) =>
renderSkeleton(`completed-${i}`)
)
@@ -97,4 +98,6 @@ const TaskList: React.FC = ({
);
};
-export default TaskList;
+const MemoizedTaskList = React.memo(TaskList);
+MemoizedTaskList.displayName = 'TaskList';
+export default MemoizedTaskList;
diff --git a/src/frontend/src/components/content/contoso.tsx b/src/App/src/components/content/contoso.tsx
similarity index 100%
rename from src/frontend/src/components/content/contoso.tsx
rename to src/App/src/components/content/contoso.tsx
diff --git a/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx b/src/App/src/components/content/streaming/StreamingAgentMessage.tsx
similarity index 97%
rename from src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx
rename to src/App/src/components/content/streaming/StreamingAgentMessage.tsx
index d23540e46..37c604558 100644
--- a/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx
+++ b/src/App/src/components/content/streaming/StreamingAgentMessage.tsx
@@ -3,9 +3,8 @@ import { AgentMessageData, AgentMessageType } from "@/models";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypePrism from "rehype-prism";
-import { Body1, Button, Tag, makeStyles, tokens } from "@fluentui/react-components";
-import { TaskService } from "@/services";
-import { Copy } from "@/coral/imports/bundleicons";
+import { Body1, Tag, makeStyles, tokens } from "@fluentui/react-components";
+import { TaskService } from "@/store";
import { PersonRegular } from "@fluentui/react-icons";
import { getAgentIcon, getAgentDisplayName } from '@/utils/agentIconUtils';
diff --git a/src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx b/src/App/src/components/content/streaming/StreamingBufferMessage.tsx
similarity index 97%
rename from src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx
rename to src/App/src/components/content/streaming/StreamingBufferMessage.tsx
index 538f2a161..6c611754c 100644
--- a/src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx
+++ b/src/App/src/components/content/streaming/StreamingBufferMessage.tsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import {
Button,
} from '@fluentui/react-components';
-import { ChevronRightRegular, ChevronDownRegular, CheckmarkCircle20Regular, ArrowTurnDownRightRegular } from '@fluentui/react-icons';
+import { CheckmarkCircle20Regular, ArrowTurnDownRightRegular } from '@fluentui/react-icons';
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypePrism from "rehype-prism";
@@ -225,4 +225,6 @@ const StreamingBufferMessage: React.FC = ({
);
};
-export default StreamingBufferMessage;
\ No newline at end of file
+const MemoizedStreamingBufferMessage = React.memo(StreamingBufferMessage);
+MemoizedStreamingBufferMessage.displayName = 'StreamingBufferMessage';
+export default MemoizedStreamingBufferMessage;
\ No newline at end of file
diff --git a/src/frontend/src/components/content/streaming/StreamingPlanResponse.tsx b/src/App/src/components/content/streaming/StreamingPlanResponse.tsx
similarity index 100%
rename from src/frontend/src/components/content/streaming/StreamingPlanResponse.tsx
rename to src/App/src/components/content/streaming/StreamingPlanResponse.tsx
diff --git a/src/frontend/src/components/content/streaming/StreamingPlanState.tsx b/src/App/src/components/content/streaming/StreamingPlanState.tsx
similarity index 100%
rename from src/frontend/src/components/content/streaming/StreamingPlanState.tsx
rename to src/App/src/components/content/streaming/StreamingPlanState.tsx
diff --git a/src/frontend/src/components/content/streaming/StreamingUserPlan.tsx b/src/App/src/components/content/streaming/StreamingUserPlan.tsx
similarity index 100%
rename from src/frontend/src/components/content/streaming/StreamingUserPlan.tsx
rename to src/App/src/components/content/streaming/StreamingUserPlan.tsx
diff --git a/src/frontend/src/components/content/streaming/StreamingUserPlanMessage.tsx b/src/App/src/components/content/streaming/StreamingUserPlanMessage.tsx
similarity index 100%
rename from src/frontend/src/components/content/streaming/StreamingUserPlanMessage.tsx
rename to src/App/src/components/content/streaming/StreamingUserPlanMessage.tsx
diff --git a/src/frontend/src/components/errors/RAIErrorCard.tsx b/src/App/src/components/errors/RAIErrorCard.tsx
similarity index 100%
rename from src/frontend/src/components/errors/RAIErrorCard.tsx
rename to src/App/src/components/errors/RAIErrorCard.tsx
diff --git a/src/frontend/src/components/errors/index.tsx b/src/App/src/components/errors/index.tsx
similarity index 100%
rename from src/frontend/src/components/errors/index.tsx
rename to src/App/src/components/errors/index.tsx
diff --git a/src/frontend/src/components/toast/InlineToaster.tsx b/src/App/src/components/toast/InlineToaster.tsx
similarity index 100%
rename from src/frontend/src/components/toast/InlineToaster.tsx
rename to src/App/src/components/toast/InlineToaster.tsx
diff --git a/src/App/src/hooks/index.tsx b/src/App/src/hooks/index.tsx
new file mode 100644
index 000000000..d00ff6ef5
--- /dev/null
+++ b/src/App/src/hooks/index.tsx
@@ -0,0 +1,5 @@
+export { default as useRAIErrorHandling } from './useRAIErrorHandling';
+export { useWebSocket } from './useWebSocket';
+export { usePlanWebSocket } from './usePlanWebSocket';
+export { usePlanActions } from './usePlanActions';
+export { useAutoScroll } from './useAutoScroll';
\ No newline at end of file
diff --git a/src/App/src/hooks/useAutoScroll.tsx b/src/App/src/hooks/useAutoScroll.tsx
new file mode 100644
index 000000000..518559bd8
--- /dev/null
+++ b/src/App/src/hooks/useAutoScroll.tsx
@@ -0,0 +1,22 @@
+/**
+ * useAutoScroll โ smooth-scrolls a container to the bottom.
+ * Extracted from PlanPage to be reusable.
+ */
+import { useCallback, useRef } from 'react';
+
+export function useAutoScroll() {
+ const messagesContainerRef = useRef(null);
+
+ const scrollToBottom = useCallback(() => {
+ setTimeout(() => {
+ messagesContainerRef.current?.scrollTo({
+ top: messagesContainerRef.current.scrollHeight,
+ behavior: 'smooth',
+ });
+ }, 100);
+ }, []);
+
+ return { messagesContainerRef, scrollToBottom };
+}
+
+export default useAutoScroll;
diff --git a/src/App/src/hooks/usePlanActions.tsx b/src/App/src/hooks/usePlanActions.tsx
new file mode 100644
index 000000000..b24a120e8
--- /dev/null
+++ b/src/App/src/hooks/usePlanActions.tsx
@@ -0,0 +1,91 @@
+/**
+ * usePlanData โ encapsulates fetching a plan by ID
+ * and dispatching the result into the Redux store.
+ * Uses createAsyncThunk (fetchPlanData) for automatic
+ * pending / fulfilled / rejected lifecycle.
+ *
+ * P1: AbortController โ cancels in-flight fetch when a new one starts
+ * or when the component unmounts, preventing stale dispatches.
+ */
+import { useCallback, useEffect, useRef } from 'react';
+import { useAppDispatch } from '@/store/hooks';
+import { ProcessedPlanData } from '@/models';
+import {
+ fetchPlanData,
+ resetPlan,
+} from '@/store/slices/planSlice';
+import {
+ setAgentMessages,
+ resetChat,
+} from '@/store/slices/chatSlice';
+import {
+ setStreamingMessageBuffer,
+ setShowBufferingText,
+ resetStreaming,
+} from '@/store/slices/streamingSlice';
+import { setWsConnected } from '@/store/slices/appSlice';
+
+/** Return type of dispatch(createAsyncThunk()) โ has .abort() */
+type ThunkPromise = ReturnType extends (...args: any[]) => infer R ? R : never;
+
+export function usePlanActions() {
+ const dispatch = useAppDispatch();
+ /** Ref holding the in-flight thunk promise so we can abort it */
+ const fetchPromiseRef = useRef> | null>(null);
+
+ /** Abort any in-flight fetch on unmount */
+ useEffect(() => {
+ return () => {
+ fetchPromiseRef.current?.abort();
+ };
+ }, []);
+
+ /** Reset every piece of plan-related state across all slices */
+ const resetPlanVariables = useCallback(() => {
+ dispatch(resetPlan());
+ dispatch(resetChat());
+ dispatch(resetStreaming());
+ dispatch(setWsConnected(false));
+ }, [dispatch]);
+
+ /**
+ * Fetch plan data from API via createAsyncThunk and hydrate cross-slice state.
+ * The core plan state (planData, loading, errorLoading) is handled
+ * automatically by extraReducers in planSlice.
+ */
+ const loadPlanData = useCallback(
+ async (planId: string, useCache = true): Promise => {
+ /* P1: Cancel any previous in-flight fetch before starting a new one */
+ fetchPromiseRef.current?.abort();
+
+ resetPlanVariables();
+
+ const promise = dispatch(fetchPlanData({ planId, useCache }));
+ fetchPromiseRef.current = promise;
+
+ const resultAction = await promise;
+
+ if (fetchPlanData.fulfilled.match(resultAction)) {
+ const planResult = resultAction.payload;
+
+ // Hydrate cross-slice state that extraReducers can't reach
+ if (planResult?.messages) {
+ dispatch(setAgentMessages(planResult.messages));
+ }
+
+ if (planResult?.streaming_message?.trim()) {
+ dispatch(setStreamingMessageBuffer(planResult.streaming_message));
+ dispatch(setShowBufferingText(true));
+ }
+
+ return planResult;
+ }
+ return null;
+ },
+ [dispatch, resetPlanVariables],
+ );
+
+ return { resetPlanVariables, loadPlanData };
+}
+
+export default usePlanActions;
diff --git a/src/frontend/src/hooks/usePlanCancellationAlert.tsx b/src/App/src/hooks/usePlanCancellationAlert.tsx
similarity index 96%
rename from src/frontend/src/hooks/usePlanCancellationAlert.tsx
rename to src/App/src/hooks/usePlanCancellationAlert.tsx
index 49f366836..67defa8c8 100644
--- a/src/frontend/src/hooks/usePlanCancellationAlert.tsx
+++ b/src/App/src/hooks/usePlanCancellationAlert.tsx
@@ -58,8 +58,7 @@ export const usePlanCancellationAlert = ({
// Navigate after successful cancellation
onNavigate();
- } catch (error) {
- console.error('โ Failed to cancel plan:', error);
+ } catch {
// Show error but still allow navigation
alert('Failed to cancel the plan properly, but navigation will continue.');
onNavigate();
diff --git a/src/App/src/hooks/usePlanWebSocket.tsx b/src/App/src/hooks/usePlanWebSocket.tsx
new file mode 100644
index 000000000..eb9faa1a3
--- /dev/null
+++ b/src/App/src/hooks/usePlanWebSocket.tsx
@@ -0,0 +1,310 @@
+/**
+ * usePlanWebSocket โ extracts all WebSocket event subscriptions
+ * from PlanPage into one reusable hook.
+ *
+ * Dispatches Redux actions for each event type so PlanPage no longer
+ * needs 7+ useEffect blocks for WebSocket handling.
+ */
+import React, { useEffect } from 'react';
+import webSocketService from '@/store/WebSocketService';
+import { PlanDataService } from '@/store/PlanDataService';
+import { useAppDispatch, useAppSelector } from '@/store/hooks';
+import {
+ setShowProcessingPlanSpinner,
+ setReloadLeftList,
+ selectPlanData,
+ selectContinueWithWebsocketFlow,
+ selectPlanApproved,
+ approvalRequestReceived,
+ planCompletedFinal,
+} from '@/store/slices/planSlice';
+import {
+ setSubmittingChatDisableInput,
+ setClarificationMessage,
+ addAgentMessage,
+} from '@/store/slices/chatSlice';
+import {
+ appendToStreamingBuffer,
+ setShowBufferingText,
+ addStreamingMessage,
+ selectStreamingMessageBuffer,
+} from '@/store/slices/streamingSlice';
+import { setWsConnected } from '@/store/slices/appSlice';
+import { setSelectedTeam } from '@/store/slices/teamSlice';
+import {
+ WebsocketMessageType,
+ MPlanData,
+ AgentMessageData,
+ AgentMessageType,
+ AgentType,
+ PlanStatus,
+ ParsedUserClarification,
+ StreamMessage,
+ ProcessedPlanData,
+} from '@/models';
+import { APIService } from '@/api/apiService';
+
+const apiService = new APIService();
+
+import { ToastIntent } from '@/components/toast/InlineToaster';
+
+interface UsePlanWebSocketProps {
+ planId: string | undefined;
+ scrollToBottom: () => void;
+ formatErrorMessage: (content: string) => string;
+ showToast: (content: React.ReactNode, intent?: ToastIntent, options?: { dismissible?: boolean; timeoutMs?: number | null }) => number;
+}
+
+/**
+ * Creates an AgentMessageResponse and persists it, then optionally reloads the task list.
+ */
+function persistAgentMessage(
+ agentMessageData: AgentMessageData,
+ planData: ProcessedPlanData | null,
+ dispatch: ReturnType,
+ isFinal = false,
+ streamingMessage = '',
+) {
+ if (!planData?.plan) return;
+
+ const agentMessageResponse = PlanDataService.createAgentMessageResponse(
+ agentMessageData,
+ planData,
+ isFinal,
+ streamingMessage,
+ );
+ apiService
+ .sendAgentMessage(agentMessageResponse)
+ .then(() => {
+ if (isFinal) {
+ setTimeout(() => dispatch(setReloadLeftList(true)), 1000);
+ }
+ })
+ .catch(() => {
+ if (isFinal) {
+ setTimeout(() => dispatch(setReloadLeftList(true)), 1000);
+ }
+ });
+}
+
+export function usePlanWebSocket({
+ planId,
+ scrollToBottom,
+ formatErrorMessage,
+ showToast,
+}: UsePlanWebSocketProps) {
+ const dispatch = useAppDispatch();
+ const planData = useAppSelector(selectPlanData);
+ const planApproved = useAppSelector(selectPlanApproved);
+ const continueWithWebsocketFlow = useAppSelector(selectContinueWithWebsocketFlow);
+ const streamingMessageBuffer = useAppSelector(selectStreamingMessageBuffer);
+
+ // โโ PLAN_APPROVAL_REQUEST โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(
+ WebsocketMessageType.PLAN_APPROVAL_REQUEST,
+ (approvalRequest: any) => {
+ let mPlanData: MPlanData | null = null;
+ if (approvalRequest.parsedData) {
+ mPlanData = approvalRequest.parsedData;
+ } else if (approvalRequest.data?.parsedData) {
+ mPlanData = approvalRequest.data.parsedData;
+ } else if (approvalRequest.data && typeof approvalRequest.data === 'object') {
+ mPlanData = approvalRequest.data;
+ } else if (approvalRequest.rawData) {
+ mPlanData = PlanDataService.parsePlanApprovalRequest(approvalRequest.rawData);
+ } else {
+ mPlanData = PlanDataService.parsePlanApprovalRequest(approvalRequest);
+ }
+ if (mPlanData) {
+ /* P0: single compound action replaces 4 separate dispatches */
+ dispatch(approvalRequestReceived(mPlanData));
+ scrollToBottom();
+ }
+ },
+ );
+ return unsub;
+ }, [dispatch, scrollToBottom]);
+
+ // โโ AGENT_MESSAGE_STREAMING โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(
+ WebsocketMessageType.AGENT_MESSAGE_STREAMING,
+ (msg: any) => {
+ const line = PlanDataService.simplifyHumanClarification(msg.data.content);
+ dispatch(setShowBufferingText(true));
+ dispatch(appendToStreamingBuffer(line));
+ },
+ );
+ return unsub;
+ }, [dispatch]);
+
+ // โโ USER_CLARIFICATION_REQUEST โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(
+ WebsocketMessageType.USER_CLARIFICATION_REQUEST,
+ (msg: any) => {
+ if (!msg) return;
+ const agentMessageData: AgentMessageData = {
+ agent: AgentType.GROUP_CHAT_MANAGER,
+ agent_type: AgentMessageType.AI_AGENT,
+ timestamp: msg.timestamp || Date.now(),
+ steps: [],
+ next_steps: [],
+ content: msg.data.question || '',
+ raw_data: msg.data || '',
+ };
+ dispatch(setClarificationMessage(msg.data as ParsedUserClarification));
+ dispatch(addAgentMessage(agentMessageData));
+ dispatch(setShowBufferingText(false));
+ dispatch(setShowProcessingPlanSpinner(false));
+ dispatch(setSubmittingChatDisableInput(false));
+ scrollToBottom();
+ persistAgentMessage(agentMessageData, planData, dispatch);
+ },
+ );
+ return unsub;
+ }, [dispatch, scrollToBottom, planData]);
+
+ // โโ AGENT_TOOL_MESSAGE (currently no-op, kept for future) โโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(WebsocketMessageType.AGENT_TOOL_MESSAGE, () => {});
+ return unsub;
+ }, []);
+
+ // โโ FINAL_RESULT_MESSAGE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(
+ WebsocketMessageType.FINAL_RESULT_MESSAGE,
+ (finalMessage: any) => {
+ if (!finalMessage) return;
+ const agentMessageData: AgentMessageData = {
+ agent: AgentType.GROUP_CHAT_MANAGER,
+ agent_type: AgentMessageType.AI_AGENT,
+ timestamp: Date.now(),
+ steps: [],
+ next_steps: [],
+ content: '\u{1F389}\u{1F389} ' + (finalMessage.data?.content || ''),
+ raw_data: finalMessage,
+ };
+ if (finalMessage?.data?.status === PlanStatus.COMPLETED) {
+ dispatch(setShowBufferingText(true));
+ dispatch(addAgentMessage(agentMessageData));
+ dispatch(setSelectedTeam(planData?.team || null));
+ /* P0: single compound action replaces setShowProcessingPlanSpinner(false) + markPlanCompleted() */
+ dispatch(planCompletedFinal());
+ scrollToBottom();
+ webSocketService.disconnect();
+ persistAgentMessage(agentMessageData, planData, dispatch, true, streamingMessageBuffer);
+ }
+ },
+ );
+ return unsub;
+ }, [dispatch, scrollToBottom, planData, streamingMessageBuffer]);
+
+ // โโ ERROR_MESSAGE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(
+ WebsocketMessageType.ERROR_MESSAGE,
+ (errorMessage: any) => {
+ let errorContent = 'An unexpected error occurred. Please try again later.';
+ if (errorMessage?.data?.data?.content) {
+ const c = errorMessage.data.data.content.trim();
+ if (c.length > 0) errorContent = c;
+ } else if (errorMessage?.data?.content) {
+ const c = errorMessage.data.content.trim();
+ if (c.length > 0) errorContent = c;
+ } else if (errorMessage?.content) {
+ const c = errorMessage.content.trim();
+ if (c.length > 0) errorContent = c;
+ } else if (typeof errorMessage === 'string') {
+ const c = errorMessage.trim();
+ if (c.length > 0) errorContent = c;
+ }
+ const errorAgent: AgentMessageData = {
+ agent: 'system',
+ agent_type: AgentMessageType.SYSTEM_AGENT,
+ timestamp: Date.now(),
+ steps: [],
+ next_steps: [],
+ content: formatErrorMessage(errorContent),
+ raw_data: errorMessage || '',
+ };
+ dispatch(addAgentMessage(errorAgent));
+ dispatch(setShowProcessingPlanSpinner(false));
+ dispatch(setShowBufferingText(false));
+ dispatch(setSubmittingChatDisableInput(false));
+ scrollToBottom();
+ showToast(errorContent, 'error');
+ },
+ );
+ return unsub;
+ }, [dispatch, scrollToBottom, showToast, formatErrorMessage]);
+
+ // โโ AGENT_MESSAGE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ const unsub = webSocketService.on(
+ WebsocketMessageType.AGENT_MESSAGE,
+ (agentMessage: any) => {
+ // Only process agent messages after the user has approved the plan
+ if (!planApproved) return;
+
+ const agentMessageData = agentMessage.data as AgentMessageData;
+ if (agentMessageData) {
+ agentMessageData.content = PlanDataService.simplifyHumanClarification(
+ agentMessageData?.content,
+ );
+ dispatch(addAgentMessage(agentMessageData));
+ dispatch(setShowProcessingPlanSpinner(true));
+ scrollToBottom();
+ persistAgentMessage(agentMessageData, planData, dispatch);
+ }
+ },
+ );
+ return unsub;
+ }, [dispatch, scrollToBottom, planData, planApproved]);
+
+ // โโ WebSocket connect / disconnect lifecycle โโโโโโโโโโโโโโโโโโ
+ useEffect(() => {
+ if (!planId || !continueWithWebsocketFlow) return;
+
+ const connectWebSocket = async () => {
+ try {
+ await webSocketService.connect(planId);
+ } catch {
+ console.log('WebSocket connection failed, continuing without real-time updates');
+ }
+ };
+ connectWebSocket();
+
+ const handleConnectionChange = (connected: boolean) => {
+ dispatch(setWsConnected(connected));
+ };
+
+ const handleStreamingMessage = (message: StreamMessage) => {
+ if (message.data?.plan_id) {
+ dispatch(addStreamingMessage(message.data));
+ }
+ };
+
+ const unsubConnection = webSocketService.on('connection_status', (msg) =>
+ handleConnectionChange(msg.data?.connected || false),
+ );
+ const unsubStreaming = webSocketService.on(
+ WebsocketMessageType.AGENT_MESSAGE,
+ handleStreamingMessage,
+ );
+ const unsubApproval = webSocketService.on(WebsocketMessageType.PLAN_APPROVAL_RESPONSE, () => {});
+ const unsubApprovalReq = webSocketService.on(WebsocketMessageType.PLAN_APPROVAL_REQUEST, () => {});
+
+ return () => {
+ unsubConnection();
+ unsubStreaming();
+ unsubApproval();
+ unsubApprovalReq();
+ webSocketService.disconnect();
+ };
+ }, [dispatch, planId, continueWithWebsocketFlow]);
+}
+
+export default usePlanWebSocket;
diff --git a/src/frontend/src/hooks/useRAIErrorHandling.tsx b/src/App/src/hooks/useRAIErrorHandling.tsx
similarity index 100%
rename from src/frontend/src/hooks/useRAIErrorHandling.tsx
rename to src/App/src/hooks/useRAIErrorHandling.tsx
diff --git a/src/frontend/src/hooks/useTeamSelection.tsx b/src/App/src/hooks/useTeamSelection.tsx
similarity index 89%
rename from src/frontend/src/hooks/useTeamSelection.tsx
rename to src/App/src/hooks/useTeamSelection.tsx
index bed0af489..9d5f6deba 100644
--- a/src/frontend/src/hooks/useTeamSelection.tsx
+++ b/src/App/src/hooks/useTeamSelection.tsx
@@ -1,6 +1,6 @@
import { useState, useCallback } from 'react';
import { TeamConfig } from '../models/Team';
-import { TeamService } from '../services/TeamService';
+import { TeamService } from '../store/TeamService';
interface UseTeamSelectionProps {
sessionId?: string;
@@ -36,13 +36,10 @@ export const useTeamSelection = ({
setError(null);
try {
- console.log('Selecting team:', team.name, 'with session ID:', sessionId);
-
const result = await TeamService.selectTeam(team.team_id);
if (result.success) {
setSelectedTeam(team);
- console.log('Team selection successful:', result.data);
// Call success callback
onTeamSelected?.(team, result.data);
@@ -61,8 +58,6 @@ export const useTeamSelection = ({
const errorMessage = err.message || 'Failed to select team';
setError(errorMessage);
- console.error('Team selection error:', err);
-
// Call error callback
onError?.(errorMessage);
diff --git a/src/frontend/src/hooks/useWebSocket.tsx b/src/App/src/hooks/useWebSocket.tsx
similarity index 94%
rename from src/frontend/src/hooks/useWebSocket.tsx
rename to src/App/src/hooks/useWebSocket.tsx
index 349eb6b98..d728524a3 100644
--- a/src/frontend/src/hooks/useWebSocket.tsx
+++ b/src/App/src/hooks/useWebSocket.tsx
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
-import { webSocketService } from '@/services';
+import { webSocketService } from '@/store';
import { StreamMessage } from '../models';
export interface WebSocketState {
@@ -47,8 +47,7 @@ export const useWebSocket = () => {
isConnecting: false,
error: null
}));
- } catch (error) {
- console.error('Failed to connect to WebSocket:', error);
+ } catch {
isConnectedRef.current = false;
isConnectingRef.current = false;
setState(prev => ({
@@ -73,8 +72,7 @@ export const useWebSocket = () => {
isReconnecting: false,
error: null
}));
- } catch (error) {
- console.error('Failed to reconnect to WebSocket:', error);
+ } catch {
isConnectedRef.current = false;
setState(prev => ({
...prev,
diff --git a/src/frontend/src/index.css b/src/App/src/index.css
similarity index 100%
rename from src/frontend/src/index.css
rename to src/App/src/index.css
diff --git a/src/frontend/src/index.tsx b/src/App/src/index.tsx
similarity index 80%
rename from src/frontend/src/index.tsx
rename to src/App/src/index.tsx
index 2e9197aa5..c38160836 100644
--- a/src/frontend/src/index.tsx
+++ b/src/App/src/index.tsx
@@ -5,8 +5,9 @@ import App from './App';
import reportWebVitals from './reportWebVitals';
import { FluentProvider, teamsLightTheme, teamsDarkTheme } from "@fluentui/react-components";
import { setEnvData, setApiUrl, config as defaultConfig, toBoolean, getUserInfo, setUserInfoGlobal } from './api/config';
-import { UserInfo } from './models';
import { apiService } from './api';
+import { Provider as ReduxProvider } from 'react-redux';
+import { store } from './store/store';
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
const AppWrapper = () => {
@@ -35,12 +36,12 @@ const AppWrapper = () => {
setEnvData(config);
setApiUrl(config.API_URL);
setConfig(config);
- let defaultUserInfo = await getUserInfo()
+ let defaultUserInfo = await getUserInfo();
window.userInfo = defaultUserInfo;
setUserInfoGlobal(defaultUserInfo);
- const browserLanguage = await apiService.sendUserBrowserLanguage();
+ await apiService.sendUserBrowserLanguage();
} catch (error) {
- console.info("frontend config did not load from python", error);
+ console.info("frontend config did not load from python", error);
} finally {
setIsConfigLoaded(true);
setIsUserInfoLoaded(true);
@@ -55,7 +56,7 @@ const AppWrapper = () => {
const handleThemeChange = (event: MediaQueryListEvent) => {
setIsDarkMode(event.matches);
- document.body.classList.toggle("dark-mode", event.matches); // โ
Add this
+ document.body.classList.toggle("dark-mode", event.matches);
};
// Apply dark-mode class initially
@@ -67,14 +68,13 @@ const AppWrapper = () => {
if (!isConfigLoaded || !isUserInfoLoaded) return Loading...
;
return (
-
-
-
+
+
+
+
+
);
};
root.render( );
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
diff --git a/src/frontend/src/models/Team.tsx b/src/App/src/models/Team.tsx
similarity index 100%
rename from src/frontend/src/models/Team.tsx
rename to src/App/src/models/Team.tsx
diff --git a/src/frontend/src/models/agentMessage.tsx b/src/App/src/models/agentMessage.tsx
similarity index 93%
rename from src/frontend/src/models/agentMessage.tsx
rename to src/App/src/models/agentMessage.tsx
index 029723706..051ce0d64 100644
--- a/src/frontend/src/models/agentMessage.tsx
+++ b/src/App/src/models/agentMessage.tsx
@@ -1,6 +1,5 @@
-import { Agent } from 'http';
import { BaseModel } from './plan';
-import { AgentMessageType, AgentType, WebsocketMessageType } from './enums';
+import { AgentMessageType, WebsocketMessageType } from './enums';
/**
* Represents a message from an agent
diff --git a/src/frontend/src/models/auth.tsx b/src/App/src/models/auth.tsx
similarity index 100%
rename from src/frontend/src/models/auth.tsx
rename to src/App/src/models/auth.tsx
diff --git a/src/frontend/src/models/enums.tsx b/src/App/src/models/enums.tsx
similarity index 98%
rename from src/frontend/src/models/enums.tsx
rename to src/App/src/models/enums.tsx
index 9c5be061f..6f0deac9a 100644
--- a/src/frontend/src/models/enums.tsx
+++ b/src/App/src/models/enums.tsx
@@ -252,11 +252,13 @@ export enum WebsocketMessageType {
REPLAN_APPROVAL_RESPONSE = "replan_approval_response",
USER_CLARIFICATION_REQUEST = "user_clarification_request",
USER_CLARIFICATION_RESPONSE = "user_clarification_response",
- FINAL_RESULT_MESSAGE = "final_result_message"
+ FINAL_RESULT_MESSAGE = "final_result_message",
+ ERROR_MESSAGE = 'error_message'
}
export enum AgentMessageType {
HUMAN_AGENT = "Human_Agent",
AI_AGENT = "AI_Agent",
+ SYSTEM_AGENT = "SYSTEM_AGENT"
}
diff --git a/src/frontend/src/models/homeInput.tsx b/src/App/src/models/homeInput.tsx
similarity index 100%
rename from src/frontend/src/models/homeInput.tsx
rename to src/App/src/models/homeInput.tsx
diff --git a/src/frontend/src/models/index.tsx b/src/App/src/models/index.tsx
similarity index 100%
rename from src/frontend/src/models/index.tsx
rename to src/App/src/models/index.tsx
diff --git a/src/frontend/src/models/inputTask.tsx b/src/App/src/models/inputTask.tsx
similarity index 100%
rename from src/frontend/src/models/inputTask.tsx
rename to src/App/src/models/inputTask.tsx
diff --git a/src/frontend/src/models/messages.tsx b/src/App/src/models/messages.tsx
similarity index 100%
rename from src/frontend/src/models/messages.tsx
rename to src/App/src/models/messages.tsx
diff --git a/src/frontend/src/models/plan.tsx b/src/App/src/models/plan.tsx
similarity index 100%
rename from src/frontend/src/models/plan.tsx
rename to src/App/src/models/plan.tsx
diff --git a/src/frontend/src/models/planPanelLeft.tsx b/src/App/src/models/planPanelLeft.tsx
similarity index 92%
rename from src/frontend/src/models/planPanelLeft.tsx
rename to src/App/src/models/planPanelLeft.tsx
index 7c9d744c7..3dbbb211a 100644
--- a/src/frontend/src/models/planPanelLeft.tsx
+++ b/src/App/src/models/planPanelLeft.tsx
@@ -9,4 +9,5 @@ export interface PlanPanelLefProps {
isHomePage: boolean;
selectedTeam?: TeamConfig | null;
onNavigationWithAlert?: (navigationFn: () => void) => void;
+ isLoadingTeam?: boolean;
}
\ No newline at end of file
diff --git a/src/frontend/src/models/taskDetails.tsx b/src/App/src/models/taskDetails.tsx
similarity index 92%
rename from src/frontend/src/models/taskDetails.tsx
rename to src/App/src/models/taskDetails.tsx
index e68ef4ca5..7759d8862 100644
--- a/src/frontend/src/models/taskDetails.tsx
+++ b/src/App/src/models/taskDetails.tsx
@@ -21,7 +21,7 @@ export interface Human {
}
export interface PlanDetailsProps {
- planData: ProcessedPlanData;
+ planData: ProcessedPlanData | null;
loading: boolean;
planApprovalRequest: MPlanData | null;
}
\ No newline at end of file
diff --git a/src/frontend/src/models/taskList.tsx b/src/App/src/models/taskList.tsx
similarity index 89%
rename from src/frontend/src/models/taskList.tsx
rename to src/App/src/models/taskList.tsx
index f9ff310f4..77d7765bd 100644
--- a/src/frontend/src/models/taskList.tsx
+++ b/src/App/src/models/taskList.tsx
@@ -10,4 +10,5 @@ export interface TaskListProps {
onTaskSelect: (taskId: string) => void;
loading?: boolean;
selectedTaskId?: string;
+ isLoadingTeam?: boolean;
}
\ No newline at end of file
diff --git a/src/App/src/pages/HomePage.tsx b/src/App/src/pages/HomePage.tsx
new file mode 100644
index 000000000..a143e3d48
--- /dev/null
+++ b/src/App/src/pages/HomePage.tsx
@@ -0,0 +1,174 @@
+import React, { useEffect, useCallback } from 'react';
+import { Spinner } from '@fluentui/react-components';
+import '../styles/PlanPage.css';
+import CoralShellColumn from '../commonComponents/components/Layout/CoralShellColumn';
+import CoralShellRow from '../commonComponents/components/Layout/CoralShellRow';
+import Content from '../commonComponents/components/Content/Content';
+import HomeInput from '@/components/content/HomeInput';
+import { NewTaskService } from '../store/NewTaskService';
+import PlanPanelLeft from '@/components/content/PlanPanelLeft';
+import ContentToolbar from '@/commonComponents/components/Content/ContentToolbar';
+import { TeamConfig } from '../models/Team';
+import { TeamService } from '../store/TeamService';
+import InlineToaster, { useInlineToaster } from '../components/toast/InlineToaster';
+import { useAppDispatch, useAppSelector } from '../store/hooks';
+import {
+ selectSelectedTeam,
+ selectIsLoadingTeam,
+ setSelectedTeam,
+ setIsLoadingTeam,
+} from '../store/slices/teamSlice';
+import { selectReloadLeftList, setReloadLeftList } from '../store/slices/planSlice';
+
+/**
+ * HomePage component - displays task lists and provides navigation
+ * Accessible via the route "/"
+ */
+const HomePage: React.FC = () => {
+ const dispatch = useAppDispatch();
+ const { showToast } = useInlineToaster();
+ const selectedTeam = useAppSelector(selectSelectedTeam);
+ const isLoadingTeam = useAppSelector(selectIsLoadingTeam);
+ const reloadLeftList = useAppSelector(selectReloadLeftList);
+
+ useEffect(() => {
+ const initTeam = async () => {
+ dispatch(setIsLoadingTeam(true));
+ try {
+ const initResponse = await TeamService.initializeTeam();
+
+ if (initResponse.data?.status === 'Request started successfully' && initResponse.data?.team_id) {
+ const teams = await TeamService.getUserTeams();
+ const initializedTeam = teams.find(team => team.team_id === initResponse.data?.team_id);
+
+ if (initializedTeam) {
+ dispatch(setSelectedTeam(initializedTeam));
+ TeamService.storageTeam(initializedTeam);
+ showToast(
+ `${initializedTeam.name} team initialized successfully with ${initializedTeam.agents?.length || 0} agents`,
+ 'success',
+ );
+ } else if (teams.length > 0) {
+ const defaultTeam = teams[0];
+ dispatch(setSelectedTeam(defaultTeam));
+ TeamService.storageTeam(defaultTeam);
+ showToast(`${defaultTeam.name} team loaded as default`, 'success');
+ }
+ } else if (initResponse.data?.requires_team_upload) {
+ dispatch(setSelectedTeam(null));
+ showToast('Welcome! Please upload a team configuration file to get started.', 'info');
+ } else if (!initResponse.success) {
+ // API call failed โ surface the error
+ console.error('Team init failed:', initResponse.error);
+ showToast(initResponse.error || 'Team initialization failed. Please try again.', 'warning');
+ }
+ } catch (error) {
+ console.error('Team initialization error:', error);
+ showToast('Team initialization failed. You can still upload a custom team configuration.', 'info');
+ dispatch(setSelectedTeam(null));
+ } finally {
+ dispatch(setIsLoadingTeam(false));
+ }
+ };
+
+ initTeam();
+ }, [dispatch]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ const handleNewTaskButton = useCallback(() => {
+ NewTaskService.handleNewTaskFromHome();
+ }, []);
+
+ const handleTeamSelect = useCallback(
+ async (team: TeamConfig | null) => {
+ dispatch(setSelectedTeam(team));
+ dispatch(setReloadLeftList(true));
+ if (team) {
+ try {
+ dispatch(setIsLoadingTeam(true));
+ const initResponse = await TeamService.initializeTeam(true);
+
+ if (initResponse.data?.status === 'Request started successfully' && initResponse.data?.team_id) {
+ const teams = await TeamService.getUserTeams();
+ const initializedTeam = teams.find(t => t.team_id === initResponse.data?.team_id);
+
+ if (initializedTeam) {
+ dispatch(setSelectedTeam(initializedTeam));
+ TeamService.storageTeam(initializedTeam);
+ dispatch(setReloadLeftList(true));
+ showToast(
+ `${initializedTeam.name} team initialized successfully with ${initializedTeam.agents?.length || 0} agents`,
+ 'success',
+ );
+ }
+ } else if (initResponse.data?.requires_team_upload) {
+ dispatch(setSelectedTeam(null));
+ showToast('No teams are configured. Please upload a team configuration to continue.', 'info');
+ } else {
+ throw new Error('Invalid response from init_team endpoint');
+ }
+ } catch {
+ showToast('Error switching team. Please try again.', 'warning');
+ } finally {
+ dispatch(setIsLoadingTeam(false));
+ }
+ } else {
+ showToast('No team is currently selected', 'info');
+ }
+ },
+ [dispatch, showToast],
+ );
+
+ const handleTeamUpload = useCallback(async () => {
+ try {
+ const teams = await TeamService.getUserTeams();
+ if (teams.length > 0) {
+ const hrTeam = teams.find(team => team.name === 'Human Resources Team');
+ const defaultTeam = hrTeam || teams[0];
+ dispatch(setSelectedTeam(defaultTeam));
+ showToast(`Team uploaded successfully! ${defaultTeam.name} remains your default team.`, 'success');
+ }
+ } catch {
+ console.error('Team upload failed');
+ }
+ }, [dispatch, showToast]);
+
+ return (
+ <>
+
+
+
+
+
+
+ {!isLoadingTeam ? (
+
+ ) : (
+
+
+
+ )}
+
+
+
+ >
+ );
+};
+
+const MemoizedHomePage = React.memo(HomePage);
+MemoizedHomePage.displayName = 'HomePage';
+export default MemoizedHomePage;
\ No newline at end of file
diff --git a/src/App/src/pages/PlanPage.tsx b/src/App/src/pages/PlanPage.tsx
new file mode 100644
index 000000000..981323e9a
--- /dev/null
+++ b/src/App/src/pages/PlanPage.tsx
@@ -0,0 +1,397 @@
+import React, { useCallback, useEffect} from 'react';
+import { useParams, useNavigate } from 'react-router-dom';
+import { Spinner, Text } from '@fluentui/react-components';
+
+/* โโ Services / API โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+import { APIService } from '../api/apiService';
+import { PlanDataService } from '../store/PlanDataService';
+import webSocketService from '../store/WebSocketService';
+
+/* โโ Models โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+import {
+ AgentMessageData,
+ AgentMessageType,
+} from '../models';
+
+/* โโ Redux โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+import { useAppDispatch, useAppSelector } from '../store/hooks';
+import {
+ selectPlanData,
+ selectPlanLoading,
+ selectErrorLoading,
+ selectPlanApprovalRequest,
+ selectProcessingApproval,
+ selectShowApprovalButtons,
+ selectShowProcessingPlanSpinner,
+ selectShowCancellationDialog,
+ selectCancellingPlan,
+ selectLoadingMessage,
+ selectReloadLeftList,
+ selectWaitingForPlan,
+ setReloadLeftList,
+ setProcessingApproval,
+ setShowProcessingPlanSpinner,
+ setShowCancellationDialog,
+ setCancellingPlan,
+ setLoadingMessage,
+ setErrorLoading,
+ planApprovalAccepted,
+ planApprovalRejected,
+} from '../store/slices/planSlice';
+import {
+ selectInput,
+ selectSubmittingChatDisable,
+ selectClarificationMessage,
+ selectAgentMessages,
+ setInput,
+ setSubmittingChatDisableInput,
+ addAgentMessage,
+} from '../store/slices/chatSlice';
+import {
+ selectStreamingMessages,
+ selectStreamingMessageBuffer,
+ selectShowBufferingText,
+} from '../store/slices/streamingSlice';
+import { selectWsConnected } from '../store/slices/appSlice';
+import { selectSelectedTeam } from '../store/slices/teamSlice';
+
+/* โโ Custom Hooks โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+import { usePlanWebSocket } from '../hooks/usePlanWebSocket';
+import { usePlanActions } from '../hooks/usePlanActions';
+import { useAutoScroll } from '../hooks/useAutoScroll';
+import { usePlanCancellationAlert } from '../hooks/usePlanCancellationAlert';
+
+/* โโ Components โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+import PlanChat from '../components/content/PlanChat';
+import PlanPanelRight from '../components/content/PlanPanelRight';
+import PlanPanelLeft from '../components/content/PlanPanelLeft';
+import CoralShellColumn from '../commonComponents/components/Layout/CoralShellColumn';
+import CoralShellRow from '../commonComponents/components/Layout/CoralShellRow';
+import Content from '../commonComponents/components/Content/Content';
+import ContentToolbar from '../commonComponents/components/Content/ContentToolbar';
+import { useInlineToaster } from '../components/toast/InlineToaster';
+import Octo from '../commonComponents/imports/Octopus.png';
+import LoadingMessage, { loadingMessages } from '../commonComponents/components/LoadingMessage';
+import PlanCancellationDialog from '../components/common/PlanCancellationDialog';
+import '../styles/PlanPage.css';
+
+// Singleton API service
+const apiService = new APIService();
+
+/* ================================================================
+ * PlanPage โ refactored to use Redux + extracted hooks
+ * ================================================================ */
+const PlanPage: React.FC = () => {
+ const { planId } = useParams<{ planId: string }>();
+ const navigate = useNavigate();
+ const dispatch = useAppDispatch();
+ const { showToast, dismissToast } = useInlineToaster();
+ const { messagesContainerRef, scrollToBottom } = useAutoScroll();
+ const { loadPlanData, resetPlanVariables } = usePlanActions();
+
+ /* โโ Redux Selectors (granular โ Point 10) โโโโโโโโโโโโโโโโ */
+ const planData = useAppSelector(selectPlanData);
+ const loading = useAppSelector(selectPlanLoading);
+ const errorLoading = useAppSelector(selectErrorLoading);
+ const planApprovalRequest = useAppSelector(selectPlanApprovalRequest);
+ const processingApproval = useAppSelector(selectProcessingApproval);
+ const showApprovalButtons = useAppSelector(selectShowApprovalButtons);
+ const showProcessingPlanSpinner = useAppSelector(selectShowProcessingPlanSpinner);
+ const showCancellationDialog = useAppSelector(selectShowCancellationDialog);
+ const cancellingPlan = useAppSelector(selectCancellingPlan);
+ const loadingMessage = useAppSelector(selectLoadingMessage);
+ const reloadLeftList = useAppSelector(selectReloadLeftList);
+ const waitingForPlan = useAppSelector(selectWaitingForPlan);
+ const input = useAppSelector(selectInput);
+ const submittingChatDisableInput = useAppSelector(selectSubmittingChatDisable);
+ const clarificationMessage = useAppSelector(selectClarificationMessage);
+ const agentMessages = useAppSelector(selectAgentMessages);
+ const streamingMessages = useAppSelector(selectStreamingMessages);
+ const streamingMessageBuffer = useAppSelector(selectStreamingMessageBuffer);
+ const showBufferingText = useAppSelector(selectShowBufferingText);
+ const wsConnected = useAppSelector(selectWsConnected);
+ const selectedTeam = useAppSelector(selectSelectedTeam);
+
+ /* โโ Cancellation alert hook โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ const [pendingNavigation, setPendingNavigation] = React.useState<(() => void) | null>(null);
+
+ const { isPlanActive } = usePlanCancellationAlert({
+ planData,
+ planApprovalRequest,
+ onNavigate: pendingNavigation || (() => {}),
+ });
+
+ /* โโ Memoized formatErrorMessage โโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ const formatErrorMessage = useCallback((content: string): string => {
+ const lines = content.split('\n');
+ return lines
+ .map((line, idx) => {
+ if (idx === 0) return `\u26A0\uFE0F ${line}`;
+ if (line.trim() === '') return '';
+ return ` ${line}`;
+ })
+ .join('\n');
+ }, []);
+
+ /* โโ WebSocket subscriptions (extracted hook) โโโโโโโโโโโโโ */
+ usePlanWebSocket({ planId, scrollToBottom, formatErrorMessage, showToast });
+
+ /* โโ Navigation with cancellation check โโโโโโโโโโโโโโโโโโโ */
+ const handleNavigationWithAlert = useCallback(
+ (navigationFn: () => void) => {
+ if (!isPlanActive()) {
+ navigationFn();
+ return;
+ }
+ setPendingNavigation(() => navigationFn);
+ dispatch(setShowCancellationDialog(true));
+ },
+ [isPlanActive, dispatch],
+ );
+
+ const handleConfirmCancellation = useCallback(async () => {
+ dispatch(setCancellingPlan(true));
+ try {
+ if (planApprovalRequest?.id) {
+ await apiService.approvePlan({
+ m_plan_id: planApprovalRequest.id,
+ plan_id: planData?.plan?.id ?? '',
+ approved: false,
+ feedback: 'Plan cancelled by user navigation',
+ });
+ }
+ pendingNavigation?.();
+ webSocketService.disconnect();
+ } catch {
+ showToast('Failed to cancel the plan properly, but navigation will continue.', 'error');
+ pendingNavigation?.();
+ } finally {
+ dispatch(setCancellingPlan(false));
+ dispatch(setShowCancellationDialog(false));
+ setPendingNavigation(null);
+ }
+ }, [planApprovalRequest, planData, pendingNavigation, showToast, dispatch]);
+
+ const handleCancelDialog = useCallback(() => {
+ dispatch(setShowCancellationDialog(false));
+ setPendingNavigation(null);
+ }, [dispatch]);
+
+ /* โโ Plan Approval / Rejection โโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ const handleApprovePlan = useCallback(async () => {
+ if (!planApprovalRequest) return;
+ dispatch(setProcessingApproval(true));
+ const id = showToast('Submitting Approval', 'progress');
+ try {
+ await apiService.approvePlan({
+ m_plan_id: planApprovalRequest.id,
+ plan_id: planData?.plan?.id ?? '',
+ approved: true,
+ feedback: 'Plan approved by user',
+ });
+ dismissToast(id);
+ /* P0: single compound action replaces 3 separate dispatches */
+ dispatch(planApprovalAccepted());
+ } catch {
+ dismissToast(id);
+ showToast('Failed to submit approval', 'error');
+ } finally {
+ dispatch(setProcessingApproval(false));
+ }
+ }, [planApprovalRequest, planData, showToast, dismissToast, dispatch]);
+
+ const handleRejectPlan = useCallback(async () => {
+ if (!planApprovalRequest) return;
+ dispatch(setProcessingApproval(true));
+ const id = showToast('Submitting cancellation', 'progress');
+ try {
+ await apiService.approvePlan({
+ m_plan_id: planApprovalRequest.id,
+ plan_id: planData?.plan?.id ?? '',
+ approved: false,
+ feedback: 'Plan rejected by user',
+ });
+ dismissToast(id);
+ navigate('/');
+ } catch {
+ dismissToast(id);
+ showToast('Failed to submit cancellation', 'error');
+ navigate('/');
+ } finally {
+ /* P0: single compound action replaces multiple state resets */
+ dispatch(planApprovalRejected());
+ }
+ }, [planApprovalRequest, planData, navigate, showToast, dismissToast, dispatch]);
+
+ /* โโ Chat submission โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ const handleOnchatSubmit = useCallback(
+ async (chatInput: string) => {
+ if (!chatInput.trim()) {
+ showToast('Please enter a clarification', 'error');
+ return;
+ }
+ dispatch(setInput(''));
+ if (!planData?.plan) return;
+ dispatch(setSubmittingChatDisableInput(true));
+ const id = showToast('Submitting clarification', 'progress');
+ try {
+ await PlanDataService.submitClarification({
+ request_id: clarificationMessage?.request_id || '',
+ answer: chatInput,
+ plan_id: planData.plan.id,
+ m_plan_id: planApprovalRequest?.id || '',
+ });
+ dispatch(setInput(''));
+ dismissToast(id);
+ showToast('Clarification submitted successfully', 'success');
+ const agentMessageData: AgentMessageData = {
+ agent: 'human',
+ agent_type: AgentMessageType.HUMAN_AGENT,
+ timestamp: Date.now(),
+ steps: [],
+ next_steps: [],
+ content: chatInput,
+ raw_data: chatInput,
+ };
+ dispatch(addAgentMessage(agentMessageData));
+ dispatch(setSubmittingChatDisableInput(true));
+ dispatch(setShowProcessingPlanSpinner(true));
+ scrollToBottom();
+ } catch {
+ dispatch(setShowProcessingPlanSpinner(false));
+ dismissToast(id);
+ dispatch(setSubmittingChatDisableInput(false));
+ showToast('Failed to submit clarification', 'error');
+ }
+ },
+ [planData, clarificationMessage, planApprovalRequest, showToast, dismissToast, dispatch, scrollToBottom],
+ );
+
+ /* โโ Left-panel handlers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ const handleNewTaskButton = useCallback(() => {
+ handleNavigationWithAlert(() => navigate('/', { state: { focusInput: true } }));
+ }, [navigate, handleNavigationWithAlert]);
+
+ const resetReload = useCallback(() => {
+ dispatch(setReloadLeftList(false));
+ }, [dispatch]);
+
+ /* โโ Loading message rotation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ useEffect(() => {
+ if (!loading) return;
+ let index = 0;
+ dispatch(setLoadingMessage(loadingMessages[0]));
+ const interval = setInterval(() => {
+ index = (index + 1) % loadingMessages.length;
+ dispatch(setLoadingMessage(loadingMessages[index]));
+ }, 3000);
+ return () => clearInterval(interval);
+ }, [loading, dispatch]);
+
+ /* โโ Initial plan load โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ useEffect(() => {
+ if (!planId) {
+ resetPlanVariables();
+ dispatch(setErrorLoading(true));
+ return;
+ }
+ loadPlanData(planId, false);
+ }, [planId, loadPlanData, resetPlanVariables, dispatch]);
+
+ /* โโ Render: Error state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ if (errorLoading) {
+ return (
+
+
+ {}}
+ onTeamUpload={async () => {}}
+ isHomePage={false}
+ selectedTeam={selectedTeam}
+ onNavigationWithAlert={handleNavigationWithAlert}
+ />
+
+
+ An error occurred while loading the plan
+
+
+
+
+ );
+ }
+
+ /* โโ Render: Normal state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+ return (
+
+
+ {}}
+ onTeamUpload={async () => {}}
+ isHomePage={false}
+ selectedTeam={selectedTeam}
+ onNavigationWithAlert={handleNavigationWithAlert}
+ />
+
+
+ {loading || !planData ? (
+ <>
+
+
+ Loading plan data...
+
+
+ >
+ ) : (
+ <>
+
+ dispatch(setInput(val))}
+ submittingChatDisableInput={submittingChatDisableInput}
+ input={input}
+ streamingMessages={streamingMessages}
+ wsConnected={wsConnected}
+ planApprovalRequest={planApprovalRequest}
+ waitingForPlan={waitingForPlan}
+ messagesContainerRef={messagesContainerRef}
+ streamingMessageBuffer={streamingMessageBuffer}
+ showBufferingText={showBufferingText}
+ agentMessages={agentMessages}
+ showProcessingPlanSpinner={showProcessingPlanSpinner}
+ showApprovalButtons={showApprovalButtons}
+ processingApproval={processingApproval}
+ handleApprovePlan={handleApprovePlan}
+ handleRejectPlan={handleRejectPlan}
+ />
+ >
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+const MemoizedPlanPage = React.memo(PlanPage);
+MemoizedPlanPage.displayName = 'PlanPage';
+export default MemoizedPlanPage;
diff --git a/src/frontend/src/pages/index.tsx b/src/App/src/pages/index.tsx
similarity index 100%
rename from src/frontend/src/pages/index.tsx
rename to src/App/src/pages/index.tsx
diff --git a/src/frontend/src/react-app-env.d.ts b/src/App/src/react-app-env.d.ts
similarity index 100%
rename from src/frontend/src/react-app-env.d.ts
rename to src/App/src/react-app-env.d.ts
diff --git a/src/frontend/src/reportWebVitals.ts b/src/App/src/reportWebVitals.ts
similarity index 100%
rename from src/frontend/src/reportWebVitals.ts
rename to src/App/src/reportWebVitals.ts
diff --git a/src/frontend/src/reportWebVitals.tsx b/src/App/src/reportWebVitals.tsx
similarity index 100%
rename from src/frontend/src/reportWebVitals.tsx
rename to src/App/src/reportWebVitals.tsx
diff --git a/src/frontend/src/setupTests.tsx b/src/App/src/setupTests.tsx
similarity index 100%
rename from src/frontend/src/setupTests.tsx
rename to src/App/src/setupTests.tsx
diff --git a/src/frontend/src/services/NewTaskService.tsx b/src/App/src/store/NewTaskService.tsx
similarity index 100%
rename from src/frontend/src/services/NewTaskService.tsx
rename to src/App/src/store/NewTaskService.tsx
diff --git a/src/frontend/src/services/PlanDataService.tsx b/src/App/src/store/PlanDataService.tsx
similarity index 96%
rename from src/frontend/src/services/PlanDataService.tsx
rename to src/App/src/store/PlanDataService.tsx
index e2c2b9e02..988428c48 100644
--- a/src/frontend/src/services/PlanDataService.tsx
+++ b/src/App/src/store/PlanDataService.tsx
@@ -1,9 +1,6 @@
import {
-
- AgentType,
ProcessedPlanData,
MPlanData,
- StepStatus,
WebsocketMessageType,
ParsedUserClarification,
AgentMessageType,
@@ -41,10 +38,9 @@ export class PlanDataService {
try {
// Use optimized getPlanById method for better performance
const planBody = await apiService.getPlanById(planId, useCache);
- console.log('Raw plan data fetched:', planBody);
return this.processPlanData(planBody);
} catch (error) {
- console.log("Failed to fetch plan data:", error);
+ console.error("Failed to fetch plan data:", error);
throw error;
}
}
@@ -233,7 +229,7 @@ export class PlanDataService {
streaming_message: string = ''
): AgentMessageResponse {
if (!planData || !planData.plan) {
- console.log("Invalid plan data provided to createAgentMessageResponse");
+ console.warn("Invalid plan data provided to createAgentMessageResponse");
}
return {
plan_id: planData.plan.plan_id,
@@ -267,7 +263,7 @@ export class PlanDataService {
try {
return apiService.submitClarification(request_id, answer, plan_id, m_plan_id);
} catch (error) {
- console.log("Failed to submit clarification:", error);
+ console.error("Failed to submit clarification:", error);
throw error;
}
}
@@ -768,6 +764,23 @@ export class PlanDataService {
*/
static parseUserClarificationRequest(rawData: any): ParsedUserClarification | null {
try {
+ // First try direct JSON extraction (clean dict format from backend)
+ const extractDirect = (val: any, depth = 0): ParsedUserClarification | null => {
+ if (depth > 10 || !val || typeof val !== 'object') return null;
+ if (typeof val.question === 'string' && typeof val.request_id === 'string') {
+ return {
+ type: WebsocketMessageType.USER_CLARIFICATION_REQUEST,
+ question: val.question.trim(),
+ request_id: val.request_id,
+ };
+ }
+ if (val.data !== undefined) return extractDirect(val.data, depth + 1);
+ return null;
+ };
+ const direct = extractDirect(rawData);
+ if (direct) return direct;
+
+ // Fallback: extract from Python repr string (legacy format)
const extractString = (val: any, depth = 0): string | null => {
if (depth > 15) return null;
if (typeof val === 'string') {
diff --git a/src/frontend/src/services/TaskService.tsx b/src/App/src/store/TaskService.tsx
similarity index 100%
rename from src/frontend/src/services/TaskService.tsx
rename to src/App/src/store/TaskService.tsx
diff --git a/src/frontend/src/services/TeamService.tsx b/src/App/src/store/TeamService.tsx
similarity index 88%
rename from src/frontend/src/services/TeamService.tsx
rename to src/App/src/store/TeamService.tsx
index 20f9979c8..6241bfa16 100644
--- a/src/frontend/src/services/TeamService.tsx
+++ b/src/App/src/store/TeamService.tsx
@@ -5,7 +5,7 @@ export class TeamService {
/**
* Upload a custom team configuration
*/
- private static readonly STORAGE_KEY = 'macae.v3.customTeam';
+ private static readonly STORAGE_KEY = 'macae.v4.customTeam';
static storageTeam(team: TeamConfig): boolean {
// Persist a TeamConfig to localStorage (browser-only).
@@ -27,27 +27,24 @@ export class TeamService {
success: boolean;
data?: {
status: string;
- team_id: string;
+ team_id?: string;
+ team?: any;
+ requires_team_upload?: boolean;
};
error?: string;
}> {
try {
- console.log('Calling /v3/init_team endpoint...');
- const response = await apiClient.get('/v3/init_team', {
+ const response = await apiClient.get('/v4/init_team', {
params: {
team_switched
}
});
- console.log('Team initialization response:', response);
-
return {
success: true,
data: response
};
} catch (error: any) {
- console.error('Team initialization failed:', error);
-
let errorMessage = 'Failed to initialize team';
if (error.response?.data?.detail) {
@@ -56,6 +53,7 @@ export class TeamService {
errorMessage = error.message;
}
+ console.error('TeamService.initializeTeam failed:', errorMessage);
return {
success: false,
error: errorMessage
@@ -81,8 +79,7 @@ export class TeamService {
try {
const formData = new FormData();
formData.append('file', teamFile);
- console.log(formData);
- const response = await apiClient.upload('/v3/upload_team_config', formData);
+ const response = await apiClient.upload('/v4/upload_team_config', formData);
return {
success: true,
@@ -135,13 +132,13 @@ export class TeamService {
*/
static async getUserTeams(): Promise {
try {
- const response = await apiClient.get('/v3/team_configs');
+ const response = await apiClient.get('/v4/team_configs');
// The apiClient returns the response data directly, not wrapped in a data property
const teams = Array.isArray(response) ? response : [];
return teams;
- } catch (error: any) {
+ } catch {
return [];
}
}
@@ -154,7 +151,7 @@ export class TeamService {
const teams = await this.getUserTeams();
const team = teams.find(t => t.team_id === teamId);
return team || null;
- } catch (error: any) {
+ } catch {
return null;
}
}
@@ -164,9 +161,9 @@ export class TeamService {
*/
static async deleteTeam(teamId: string): Promise {
try {
- const response = await apiClient.delete(`/v3/team_configs/${teamId}`);
+ await apiClient.delete(`/v4/team_configs/${teamId}`);
return true;
- } catch (error: any) {
+ } catch {
return false;
}
}
@@ -180,7 +177,7 @@ export class TeamService {
error?: string;
}> {
try {
- const response = await apiClient.post('/v3/select_team', {
+ const response = await apiClient.post('/v4/select_team', {
team_id: teamId,
});
@@ -234,12 +231,12 @@ export class TeamService {
}
}
- const isProxyAgent = agent.name && agent.name.toLowerCase() === 'proxyagent';
+ const isProxyAgent = agent.name && agent.name.toLowerCase() === 'proxyagent';
- // Deployment name validation (skip for proxy agents)
- if (!isProxyAgent && !agent.deployment_name) {
- errors.push(`Agent ${index + 1} (${agent.name}): Missing required field: deployment_name (required for non-proxy agents)`);
- }
+ // Deployment name validation (skip for proxy agents)
+ if (!isProxyAgent && !agent.deployment_name) {
+ errors.push(`Agent ${index + 1} (${agent.name}): Missing required field: deployment_name (required for non-proxy agents)`);
+ }
// RAG agent validation
diff --git a/src/frontend/src/services/WebSocketService.tsx b/src/App/src/store/WebSocketService.tsx
similarity index 78%
rename from src/frontend/src/services/WebSocketService.tsx
rename to src/App/src/store/WebSocketService.tsx
index 2bdb5f87d..7fff80a56 100644
--- a/src/frontend/src/services/WebSocketService.tsx
+++ b/src/App/src/store/WebSocketService.tsx
@@ -1,17 +1,20 @@
-import { getApiUrl, getUserId, headerBuilder } from '../api/config';
+import { getApiUrl, getUserId } from '../api/config';
import { PlanDataService } from './PlanDataService';
-import { MPlanData, ParsedPlanApprovalRequest, StreamingPlanUpdate, StreamMessage, WebsocketMessageType } from '../models';
+import { ParsedPlanApprovalRequest, StreamingPlanUpdate, StreamMessage, WebsocketMessageType } from '../models';
class WebSocketService {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
- private reconnectDelay = 12000;
+ private reconnectDelay = 1000; // 1s base, exponential: 1s, 2s, 4s, 8s, 16s
private listeners: Map void>> = new Map();
private planSubscriptions: Set = new Set();
- private reconnectTimer: NodeJS.Timeout | null = null;
+ private reconnectTimer: ReturnType | null = null;
private isConnecting = false;
+ private intentionalDisconnect = false;
+ private lastPlanId: string | undefined;
+ private lastProcessId: string | undefined;
private buildSocketUrl(processId?: string, planId?: string): string {
@@ -27,9 +30,8 @@ class WebSocketService {
// Decide path addition
let userId = getUserId();
const hasApiSegment = /\/api(\/|$)/i.test(base);
- const socketPath = hasApiSegment ? '/v3/socket' : '/api/v3/socket';
+ const socketPath = hasApiSegment ? '/v4/socket' : '/api/v4/socket';
const url = `${base}${socketPath}${processId ? `/${processId}` : `/${planId}`}?user_id=${userId || ''}`;
- console.log("Constructed WebSocket URL:", url);
return url;
}
connect(planId: string, processId?: string): Promise {
@@ -44,6 +46,9 @@ class WebSocketService {
}
try {
this.isConnecting = true;
+ this.intentionalDisconnect = false;
+ this.lastPlanId = planId;
+ this.lastProcessId = processId;
const wsUrl = this.buildSocketUrl(processId, planId);
this.ws = new WebSocket(wsUrl);
@@ -71,7 +76,9 @@ class WebSocketService {
this.isConnecting = false;
this.ws = null;
this.emit('connection_status', { connected: false });
- if (this.reconnectAttempts < this.maxReconnectAttempts && event.code !== 1000) {
+ /* P1: Only auto-reconnect if not intentional and not a clean close */
+ if (!this.intentionalDisconnect && event.code !== 1000 &&
+ this.reconnectAttempts < this.maxReconnectAttempts) {
this.attemptReconnect();
}
};
@@ -91,34 +98,37 @@ class WebSocketService {
}
disconnect(): void {
+ this.intentionalDisconnect = true;
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
this.reconnectAttempts = this.maxReconnectAttempts;
if (this.ws) {
- this.ws.close(1000, 'Manual disconnect');
+ const socket = this.ws;
this.ws = null;
+
+ // Detach handlers so no stale callbacks fire during/after close
+ socket.onopen = null;
+ socket.onmessage = null;
+ socket.onerror = null;
+ socket.onclose = null;
+
+ if (socket.readyState === WebSocket.OPEN) {
+ // Normal close
+ socket.close(1000, 'Manual disconnect');
+ } else if (socket.readyState === WebSocket.CONNECTING) {
+ // Still handshaking โ wait for open then close cleanly.
+ // This avoids the "WebSocket closed before connection established" warning.
+ socket.addEventListener('open', () => socket.close(1000, 'Manual disconnect'), { once: true });
+ socket.addEventListener('error', () => { /* handshake failed โ nothing to close */ }, { once: true });
+ }
+ // CLOSING / CLOSED โ no action needed
}
this.planSubscriptions.clear();
this.isConnecting = false;
}
- subscribeToPlan(planId: string): void {
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
- const message = { type: 'subscribe_plan', plan_id: planId };
- this.ws.send(JSON.stringify(message));
- this.planSubscriptions.add(planId);
- }
- }
-
- unsubscribeFromPlan(planId: string): void {
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
- const message = { type: 'unsubscribe_plan', plan_id: planId };
- this.ws.send(JSON.stringify(message));
- this.planSubscriptions.delete(planId);
- }
- }
on(eventType: string, callback: (message: StreamMessage) => void): () => void {
if (!this.listeners.has(eventType)) {
@@ -166,6 +176,12 @@ class WebSocketService {
});
}
+ onErrorMessage(callback: (data: any) => void): () => void {
+ return this.on(WebsocketMessageType.ERROR_MESSAGE, (message: StreamMessage) => {
+ callback(message.data);
+ });
+ }
+
private emit(eventType: string, data: any): void {
const message: StreamMessage = {
type: eventType as any,
@@ -184,7 +200,6 @@ class WebSocketService {
switch (message.type) {
case WebsocketMessageType.PLAN_APPROVAL_REQUEST: {
- console.log("Message Plan Approval Request':", message);
const parsedData = PlanDataService.parsePlanApprovalRequest(message.data);
if (parsedData) {
const structuredMessage: ParsedPlanApprovalRequest = {
@@ -201,11 +216,8 @@ class WebSocketService {
}
case WebsocketMessageType.AGENT_MESSAGE: {
- console.log("Message Agent':", message);
if (message.data) {
- console.log('WebSocket message received:', message);
const transformed = PlanDataService.parseAgentMessage(message);
- console.log('Transformed AGENT_MESSAGE:', transformed);
this.emit(WebsocketMessageType.AGENT_MESSAGE, transformed);
}
@@ -213,20 +225,16 @@ class WebSocketService {
}
case WebsocketMessageType.AGENT_MESSAGE_STREAMING: {
- console.log("Message streamming agent buffer:", message);
if (message.data) {
const streamedMessage = PlanDataService.parseAgentMessageStreaming(message);
- console.log('WebSocket AGENT_MESSAGE_STREAMING message received:', streamedMessage);
this.emit(WebsocketMessageType.AGENT_MESSAGE_STREAMING, streamedMessage);
}
break;
}
case WebsocketMessageType.USER_CLARIFICATION_REQUEST: {
- console.log("Message clarification':", message);
if (message.data) {
const transformed = PlanDataService.parseUserClarificationRequest(message);
- console.log('WebSocket USER_CLARIFICATION_REQUEST message received:', transformed);
this.emit(WebsocketMessageType.USER_CLARIFICATION_REQUEST, transformed);
}
break;
@@ -234,7 +242,6 @@ class WebSocketService {
case WebsocketMessageType.AGENT_TOOL_MESSAGE: {
- console.log("Message agent tool':", message);
if (message.data) {
//const transformed = PlanDataService.parseUserClarificationRequest(message);
this.emit(WebsocketMessageType.AGENT_TOOL_MESSAGE, message);
@@ -242,14 +249,16 @@ class WebSocketService {
break;
}
case WebsocketMessageType.FINAL_RESULT_MESSAGE: {
- console.log("Message final result':", message);
if (message.data) {
const transformed = PlanDataService.parseFinalResultMessage(message);
- console.log('WebSocket FINAL_RESULT_MESSAGE received:', transformed);
this.emit(WebsocketMessageType.FINAL_RESULT_MESSAGE, transformed);
}
break;
}
+ case WebsocketMessageType.ERROR_MESSAGE: {
+ this.emit(WebsocketMessageType.ERROR_MESSAGE, message.data); // Emit the data
+ break;
+ }
case WebsocketMessageType.USER_CLARIFICATION_RESPONSE:
case WebsocketMessageType.REPLAN_APPROVAL_REQUEST:
case WebsocketMessageType.REPLAN_APPROVAL_RESPONSE:
@@ -257,13 +266,11 @@ class WebSocketService {
case WebsocketMessageType.AGENT_STREAM_START:
case WebsocketMessageType.AGENT_STREAM_END:
case WebsocketMessageType.SYSTEM_MESSAGE: {
- console.log("Message other types':", message);
this.emit(message.type, message);
break;
}
default: {
- console.log("Message default':", message);
this.emit(message.type, message);
break;
}
@@ -277,10 +284,21 @@ class WebSocketService {
}
if (this.isConnecting || this.reconnectTimer) return;
this.reconnectAttempts++;
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
+ /* P1: exponential backoff โ 1s, 2s, 4s, 8s, 16s (capped) */
+ const delay = Math.min(
+ this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
+ 16000,
+ );
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null;
- this.emit('error', { error: 'Connection lost - manual reconnection required' });
+ if (this.intentionalDisconnect) return;
+ if (this.lastPlanId) {
+ this.connect(this.lastPlanId, this.lastProcessId).catch(() => {
+ /* If reconnect fails, onclose will trigger another attempt */
+ });
+ } else {
+ this.emit('error', { error: 'Connection lost โ no planId available for reconnection' });
+ }
}, delay);
}
@@ -309,14 +327,14 @@ class WebSocketService {
return;
}
try {
- const v3Response = {
+ const v4Response = {
m_plan_id: response.plan_id,
approved: response.approved,
feedback: response.feedback || response.user_response || response.human_clarification || '',
};
const message = {
type: WebsocketMessageType.PLAN_APPROVAL_RESPONSE,
- data: v3Response
+ data: v4Response
};
this.ws.send(JSON.stringify(message));
} catch {
diff --git a/src/App/src/store/hooks.ts b/src/App/src/store/hooks.ts
new file mode 100644
index 000000000..00f0fab73
--- /dev/null
+++ b/src/App/src/store/hooks.ts
@@ -0,0 +1,14 @@
+/**
+ * Typed Redux Hooks
+ *
+ * Pre-typed versions of useDispatch and useSelector so every component
+ * automatically gets correct RootState / AppDispatch types.
+ */
+import { useDispatch, useSelector, type TypedUseSelectorHook } from 'react-redux';
+import type { RootState, AppDispatch } from './store';
+
+/** Use throughout the app instead of plain `useDispatch` */
+export const useAppDispatch: () => AppDispatch = useDispatch;
+
+/** Use throughout the app instead of plain `useSelector` */
+export const useAppSelector: TypedUseSelectorHook = useSelector;
diff --git a/src/App/src/store/index.ts b/src/App/src/store/index.ts
new file mode 100644
index 000000000..585e1424a
--- /dev/null
+++ b/src/App/src/store/index.ts
@@ -0,0 +1,17 @@
+/**
+ * State barrel export
+ */
+export { store } from './store';
+export type { RootState, AppDispatch } from './store';
+export { useAppDispatch, useAppSelector } from './hooks';
+
+// Slice actions & selectors
+export * from './slices/planSlice';
+export * from './slices/chatSlice';
+export * from './slices/appSlice';
+export * from './slices/teamSlice';
+export * from './slices/streamingSlice';
+
+// Services
+export { default as TaskService } from './TaskService';
+export * from './WebSocketService';
diff --git a/src/App/src/store/slices/appSlice.ts b/src/App/src/store/slices/appSlice.ts
new file mode 100644
index 000000000..6fdebc915
--- /dev/null
+++ b/src/App/src/store/slices/appSlice.ts
@@ -0,0 +1,49 @@
+/**
+ * App Slice โ global application state: config, theme, WebSocket connection.
+ */
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import type { RootState } from '../store';
+
+export interface AppState {
+ /** Has the runtime config been loaded from /config? */
+ configLoaded: boolean;
+ /** Is dark mode active? */
+ isDarkMode: boolean;
+ /** Is the global WebSocket connected? */
+ wsConnected: boolean;
+}
+
+const initialState: AppState = {
+ configLoaded: false,
+ isDarkMode: window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false,
+ wsConnected: false,
+};
+
+const appSlice = createSlice({
+ name: 'app',
+ initialState,
+ reducers: {
+ setConfigLoaded(state, action: PayloadAction) {
+ state.configLoaded = action.payload;
+ },
+ setIsDarkMode(state, action: PayloadAction) {
+ state.isDarkMode = action.payload;
+ },
+ setWsConnected(state, action: PayloadAction) {
+ state.wsConnected = action.payload;
+ },
+ },
+});
+
+export const {
+ setConfigLoaded,
+ setIsDarkMode,
+ setWsConnected,
+} = appSlice.actions;
+
+/* โโ Granular Selectors โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+export const selectConfigLoaded = (s: RootState) => s.app.configLoaded;
+export const selectIsDarkMode = (s: RootState) => s.app.isDarkMode;
+export const selectWsConnected = (s: RootState) => s.app.wsConnected;
+
+export default appSlice.reducer;
diff --git a/src/App/src/store/slices/chatSlice.ts b/src/App/src/store/slices/chatSlice.ts
new file mode 100644
index 000000000..9c1ad991a
--- /dev/null
+++ b/src/App/src/store/slices/chatSlice.ts
@@ -0,0 +1,82 @@
+/**
+ * Chat Slice โ user input, submission state, agent messages,
+ * and clarification handling.
+ */
+import { createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit';
+import type { RootState } from '../store';
+import { AgentMessageData, ParsedUserClarification } from '@/models';
+
+export interface ChatState {
+ /** Current chat input value */
+ input: string;
+ /** Disable the input while a submission is in flight */
+ submittingChatDisableInput: boolean;
+ /** Clarification request from the backend */
+ clarificationMessage: ParsedUserClarification | null;
+ /** All agent messages rendered in the chat panel */
+ agentMessages: AgentMessageData[];
+}
+
+const initialState: ChatState = {
+ input: '',
+ submittingChatDisableInput: true,
+ clarificationMessage: null,
+ agentMessages: [],
+};
+
+const chatSlice = createSlice({
+ name: 'chat',
+ initialState,
+ reducers: {
+ setInput(state, action: PayloadAction) {
+ state.input = action.payload;
+ },
+ setSubmittingChatDisableInput(state, action: PayloadAction) {
+ state.submittingChatDisableInput = action.payload;
+ },
+ setClarificationMessage(state, action: PayloadAction) {
+ state.clarificationMessage = action.payload as any;
+ },
+ setAgentMessages(state, action: PayloadAction) {
+ state.agentMessages = action.payload as any;
+ },
+ addAgentMessage(state, action: PayloadAction) {
+ state.agentMessages.push(action.payload as any);
+ },
+ /** Reset chat state (used when navigating to a new plan) */
+ resetChat() {
+ return { ...initialState };
+ },
+ },
+});
+
+export const {
+ setInput,
+ setSubmittingChatDisableInput,
+ setClarificationMessage,
+ setAgentMessages,
+ addAgentMessage,
+ resetChat,
+} = chatSlice.actions;
+
+/* โโ Granular Selectors โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+export const selectInput = (s: RootState) => s.chat.input;
+export const selectSubmittingChatDisable = (s: RootState) => s.chat.submittingChatDisableInput;
+export const selectClarificationMessage = (s: RootState) => s.chat.clarificationMessage;
+export const selectAgentMessages = (s: RootState) => s.chat.agentMessages;
+
+/* โโ Memoized Derived Selectors โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+
+/** Number of agent messages (avoids re-render on array identity change when count is same) */
+export const selectAgentMessageCount = createSelector(
+ selectAgentMessages,
+ (messages) => messages.length,
+);
+
+/** Whether a clarification is currently pending */
+export const selectHasPendingClarification = createSelector(
+ selectClarificationMessage,
+ (msg) => msg !== null,
+);
+
+export default chatSlice.reducer;
diff --git a/src/App/src/store/slices/planSlice.ts b/src/App/src/store/slices/planSlice.ts
new file mode 100644
index 000000000..1d99169f7
--- /dev/null
+++ b/src/App/src/store/slices/planSlice.ts
@@ -0,0 +1,282 @@
+/**
+ * Plan Slice โ centralises all plan-level state that was previously
+ * scattered across 10+ useState calls in PlanPage.
+ */
+import { createSlice, createAsyncThunk, createSelector, PayloadAction } from '@reduxjs/toolkit';
+import type { RootState } from '../store';
+import { ProcessedPlanData, MPlanData, PlanStatus } from '@/models';
+import { PlanDataService } from '@/store/PlanDataService';
+
+/* โโ Async Thunks (Point 9 โ createAsyncThunk for APIโdriven state) โโ */
+
+/**
+ * Fetch plan data from the API by planId.
+ * Automatically dispatches pending / fulfilled / rejected actions
+ * that are handled in extraReducers below.
+ */
+export const fetchPlanData = createAsyncThunk<
+ ProcessedPlanData | null,
+ { planId: string; useCache?: boolean },
+ { rejectValue: string }
+>(
+ 'plan/fetchPlanData',
+ async ({ planId, useCache = true }, { rejectWithValue }) => {
+ try {
+ return await PlanDataService.fetchPlanData(planId, useCache);
+ } catch {
+ return rejectWithValue('Failed to load plan data');
+ }
+ },
+);
+
+export interface PlanState {
+ /** Fully processed plan (null when not loaded) */
+ planData: ProcessedPlanData | null;
+ /** Is the initial plan load in flight? */
+ loading: boolean;
+ /** Did the plan load fail? */
+ errorLoading: boolean;
+ /** Waiting for the backend to produce a plan */
+ waitingForPlan: boolean;
+ /** Plan-approval payload received from WebSocket */
+ planApprovalRequest: MPlanData | null;
+ /** Is an approval/reject API call in progress? */
+ processingApproval: boolean;
+ /** Should the approval buttons be visible? */
+ showApprovalButtons: boolean;
+ /** Show a spinner while the plan is being executed */
+ showProcessingPlanSpinner: boolean;
+ /** Should we continue with WebSocket flow? */
+ continueWithWebsocketFlow: boolean;
+ /** Has the user approved the plan (or is the plan already post-approval)? */
+ planApproved: boolean;
+ /** Trigger to reload the left-panel task list */
+ reloadLeftList: boolean;
+ /** Cancellation dialog state */
+ showCancellationDialog: boolean;
+ /** Is a cancellation API call in progress? */
+ cancellingPlan: boolean;
+ /** Loading message for spinners */
+ loadingMessage: string;
+}
+
+const initialState: PlanState = {
+ planData: null,
+ loading: true,
+ errorLoading: false,
+ waitingForPlan: true,
+ planApprovalRequest: null,
+ processingApproval: false,
+ showApprovalButtons: true,
+ showProcessingPlanSpinner: false,
+ continueWithWebsocketFlow: false,
+ planApproved: false,
+ reloadLeftList: true,
+ showCancellationDialog: false,
+ cancellingPlan: false,
+ loadingMessage: '',
+};
+
+const planSlice = createSlice({
+ name: 'plan',
+ initialState,
+ reducers: {
+ setPlanData(state, action: PayloadAction) {
+ state.planData = action.payload as any;
+ },
+ setLoading(state, action: PayloadAction) {
+ state.loading = action.payload;
+ },
+ setErrorLoading(state, action: PayloadAction) {
+ state.errorLoading = action.payload;
+ },
+ setWaitingForPlan(state, action: PayloadAction) {
+ state.waitingForPlan = action.payload;
+ },
+ setPlanApprovalRequest(state, action: PayloadAction) {
+ state.planApprovalRequest = action.payload as any;
+ },
+ setProcessingApproval(state, action: PayloadAction) {
+ state.processingApproval = action.payload;
+ },
+ setShowApprovalButtons(state, action: PayloadAction) {
+ state.showApprovalButtons = action.payload;
+ },
+ setShowProcessingPlanSpinner(state, action: PayloadAction) {
+ state.showProcessingPlanSpinner = action.payload;
+ },
+ setContinueWithWebsocketFlow(state, action: PayloadAction) {
+ state.continueWithWebsocketFlow = action.payload;
+ },
+ setPlanApproved(state, action: PayloadAction) {
+ state.planApproved = action.payload;
+ },
+ setReloadLeftList(state, action: PayloadAction) {
+ state.reloadLeftList = action.payload;
+ },
+ setShowCancellationDialog(state, action: PayloadAction) {
+ state.showCancellationDialog = action.payload;
+ },
+ setCancellingPlan(state, action: PayloadAction) {
+ state.cancellingPlan = action.payload;
+ },
+ setLoadingMessage(state, action: PayloadAction) {
+ state.loadingMessage = action.payload;
+ },
+ /** Mark plan completed and update local state in one dispatch */
+ markPlanCompleted(state) {
+ if (state.planData?.plan) {
+ (state.planData as any).plan.overall_status = PlanStatus.COMPLETED;
+ }
+ },
+
+ /* โโ Compound Actions (Optimization โ batch multiple state changes) โโ */
+
+ /** Single dispatch after user approves a plan (replaces 4 separate dispatches) */
+ planApprovalAccepted(state) {
+ state.planApproved = true;
+ state.showApprovalButtons = false;
+ state.showProcessingPlanSpinner = true;
+ state.processingApproval = false;
+ },
+ /** Single dispatch after user rejects a plan (replaces 3 separate dispatches) */
+ planApprovalRejected(state) {
+ state.planApproved = false;
+ state.showApprovalButtons = false;
+ state.showProcessingPlanSpinner = false;
+ state.processingApproval = false;
+ },
+ /** Single dispatch when PLAN_APPROVAL_REQUEST arrives via WebSocket */
+ approvalRequestReceived(state, action: PayloadAction) {
+ state.planApprovalRequest = action.payload as any;
+ state.waitingForPlan = false;
+ state.showProcessingPlanSpinner = false;
+ state.showApprovalButtons = true;
+ },
+ /** Single dispatch when FINAL_RESULT_MESSAGE arrives and plan is complete */
+ planCompletedFinal(state) {
+ state.showProcessingPlanSpinner = false;
+ if (state.planData?.plan) {
+ (state.planData as any).plan.overall_status = PlanStatus.COMPLETED;
+ }
+ },
+
+ /** Reset everything back to initial state (used when navigating to a new plan) */
+ resetPlan() {
+ return { ...initialState };
+ },
+ },
+ extraReducers: (builder) => {
+ builder
+ .addCase(fetchPlanData.pending, (state) => {
+ state.loading = true;
+ state.errorLoading = false;
+ })
+ .addCase(fetchPlanData.fulfilled, (state, action) => {
+ const planResult = action.payload;
+ state.loading = false;
+
+ if (planResult?.plan?.overall_status === PlanStatus.IN_PROGRESS) {
+ state.showApprovalButtons = true;
+ } else {
+ state.showApprovalButtons = false;
+ state.waitingForPlan = false;
+ }
+
+ if (planResult?.plan?.overall_status !== PlanStatus.COMPLETED) {
+ state.continueWithWebsocketFlow = true;
+ }
+
+ // Mark plan as already approved if it's past the approval stage
+ if (
+ planResult?.plan?.overall_status === PlanStatus.APPROVED ||
+ planResult?.plan?.overall_status === PlanStatus.COMPLETED
+ ) {
+ state.planApproved = true;
+ }
+
+ if (planResult?.mplan) {
+ state.planApprovalRequest = planResult.mplan as any;
+ }
+
+ state.planData = planResult as any;
+ })
+ .addCase(fetchPlanData.rejected, (state) => {
+ state.loading = false;
+ state.errorLoading = true;
+ state.planData = null;
+ });
+ },
+});
+
+export const {
+ setPlanData,
+ setLoading,
+ setErrorLoading,
+ setWaitingForPlan,
+ setPlanApprovalRequest,
+ setProcessingApproval,
+ setShowApprovalButtons,
+ setShowProcessingPlanSpinner,
+ setContinueWithWebsocketFlow,
+ setPlanApproved,
+ setReloadLeftList,
+ setShowCancellationDialog,
+ setCancellingPlan,
+ setLoadingMessage,
+ markPlanCompleted,
+ planApprovalAccepted,
+ planApprovalRejected,
+ approvalRequestReceived,
+ planCompletedFinal,
+ resetPlan,
+} = planSlice.actions;
+
+/* โโ Granular Selectors (Point 10) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
+export const selectPlanData = (s: RootState) => s.plan.planData;
+export const selectPlanLoading = (s: RootState) => s.plan.loading;
+export const selectErrorLoading = (s: RootState) => s.plan.errorLoading;
+export const selectWaitingForPlan = (s: RootState) => s.plan.waitingForPlan;
+export const selectPlanApprovalRequest = (s: RootState) => s.plan.planApprovalRequest;
+export const selectProcessingApproval = (s: RootState) => s.plan.processingApproval;
+export const selectShowApprovalButtons = (s: RootState) => s.plan.showApprovalButtons;
+export const selectShowProcessingPlanSpinner = (s: RootState) => s.plan.showProcessingPlanSpinner;
+export const selectContinueWithWebsocketFlow = (s: RootState) => s.plan.continueWithWebsocketFlow;
+export const selectReloadLeftList = (s: RootState) => s.plan.reloadLeftList;
+export const selectShowCancellationDialog = (s: RootState) => s.plan.showCancellationDialog;
+export const selectCancellingPlan = (s: RootState) => s.plan.cancellingPlan;
+export const selectLoadingMessage = (s: RootState) => s.plan.loadingMessage;
+export const selectPlanStatus = (s: RootState) => s.plan.planData?.plan?.overall_status ?? null;
+export const selectPlanApproved = (s: RootState) => s.plan.planApproved;
+
+/* โโ Memoized Derived Selectors (createSelector) โโโโโโโโโโโโโโโโโโโ */
+
+/** Is the plan currently active (not completed / failed / canceled)? */
+export const selectIsPlanActive = createSelector(
+ selectPlanStatus,
+ (status): boolean =>
+ status !== null &&
+ status !== PlanStatus.COMPLETED &&
+ status !== PlanStatus.FAILED &&
+ status !== PlanStatus.CANCELED,
+);
+
+/** Plan team (memoized โ avoids new reference on unrelated planData changes) */
+export const selectPlanTeam = createSelector(
+ selectPlanData,
+ (planData) => planData?.team ?? null,
+);
+
+/** Plan ID extracted from planData (avoids drilling into nested object each render) */
+export const selectPlanId = createSelector(
+ selectPlanData,
+ (planData) => planData?.plan?.id ?? null,
+);
+
+/** mplan from planData (avoids new object reference when planData changes) */
+export const selectMPlan = createSelector(
+ selectPlanData,
+ (planData) => planData?.mplan ?? null,
+);
+
+export default planSlice.reducer;
diff --git a/src/App/src/store/slices/streamingSlice.ts b/src/App/src/store/slices/streamingSlice.ts
new file mode 100644
index 000000000..52724f7cb
--- /dev/null
+++ b/src/App/src/store/slices/streamingSlice.ts
@@ -0,0 +1,76 @@
+/**
+ * Streaming Slice โ WebSocket streaming buffer and related flags.
+ */
+import { createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit';
+import type { RootState } from '../store';
+import { StreamingPlanUpdate } from '@/models';
+
+export interface StreamingState {
+ /** Streaming plan updates from WebSocket */
+ streamingMessages: StreamingPlanUpdate[];
+ /** Buffered streaming text (accumulated agent output) */
+ streamingMessageBuffer: string;
+ /** Should the buffering text indicator be visible? */
+ showBufferingText: boolean;
+}
+
+const initialState: StreamingState = {
+ streamingMessages: [],
+ streamingMessageBuffer: '',
+ showBufferingText: false,
+};
+
+const streamingSlice = createSlice({
+ name: 'streaming',
+ initialState,
+ reducers: {
+ setStreamingMessages(state, action: PayloadAction